Почему этот запрос LINQ не работает должным образом?

#c# #linq

#c# #linq

Вопрос:

Предположим, у меня есть класс с именем MyClass, который имеет два свойства (идентификатор int и строковое имя). Я хочу заполнить список этих объектов MyClass из другой коллекции, но мне нужны только уникальные. Эта другая коллекция является объектом третьей стороны, который имеет свойство с именем ‘Properties’, представляющее собой просто массив значений, первые два из которых соответствуют значениям Id и Name, которые меня интересуют. В этой коллекции могут быть дубликаты, поэтому мне нужны только уникальные.

Кажется, что это должно сработать, но это не так, он возвращает все элементы независимо от дубликатов. Что я здесь делаю не так?

 List<MyClass> items = (from MyClass mc in collectionOfProps 
select new MyClass() { 
Id = collectionOfProps.Properties[0], 
Name = collectionOfProps.Properties[1] }).Distinct().ToList();
  

Комментарии:

1. глядя на ваш запрос, мне он кажется хорошим.

Ответ №1:

Проблема, вероятно, в том, что MyClass не реализуется IEquatable<MyClass> так же хорошо, как переопределение Equals и GetHashCode .

Чтобы заставить Distinct() работать так, как вы хотите, вы должны реализовать IEquatable<T> . В противном случае для проверки используется значение по умолчанию (ссылочное равенство), что означает, что он определил бы, что элементы не различаются, только если бы они были одним и тем же экземпляром.

Комментарии:

1. Эта страница , кажется, объясняет, почему внедрение IEquatable<MyClass> не обязательно решит проблему само по себе.

2. @Brian: Ему тоже нужно GetHashCode() .

3. @SLaks: Да, но заполнение a GetHashCode в IEqualityComparer также не поможет (очевидно, что его ввод в type поможет ). В любом случае, человек в этой статье пытался избежать GetHashCode и был вынужден поступить немного некрасиво, чтобы достичь своей цели.

4. @Brian Обратите внимание, что, если вы читали документацию для IEquatable<T> , там явно упоминается, что вы всегда должны переопределять Equals и GetHashCode при реализации интерфейса. «Правильная» реализация IEquatable<T> подразумевает, что вы реализуете все три, даже если в интерфейсе определен только один метод.

5. @ReedCopsey: Вы правы. Кроме того, в документации для Distinct явно указано, что нужно реализовать IEquatable . Хотя более внимательное прочтение документации (т. Е. игнорирование краткого объяснения компаратора по умолчанию и вместо этого чтение связанной документации ) показывает, что достаточно переопределить Equals и GetHashCode .

Ответ №2:

Вам нужно переопределить Equals() и GetHashCode() сравнить экземпляры по значению.

Комментарии:

1. SLaks верен. По крайней мере, в .Net 4.0, Distinct реализуется с использованием a Set ( internal класс framework, который похож на a HashSet ).

Ответ №3:

Вы переопределили равенство (для distinct) в MyClass? Мое предположение было бы отрицательным.

Согласно документам:

http://msdn.microsoft.com/en-us/library/bb348436.aspx

Средство сравнения равенства по умолчанию, Default, используется для сравнения значений типов, которые реализуют универсальный интерфейс IEquatable (Of T). Чтобы сравнить пользовательский тип данных, вам необходимо реализовать этот интерфейс и предоставить свои собственные методы GetHashCode и Equals для этого типа.