class Program
{
static void Main(string[] args)
{
List<Book> books = new List<Book>
{
new Book
{
Name="C# in Depth",
Authors = new List<Author>
{
new Author
{
FirstName = "Jon", LastName="Skeet"
},
new Author
{
FirstName = "Jon", LastName="Skeet"
},
}
},
new Book
{
Name="LINQ in Action",
Authors = new List<Author>
{
new Author
{
FirstName = "Fabrice", LastName="Marguerie"
},
new Author
{
FirstName = "Steve", LastName="Eichert"
},
new Author
{
FirstName = "Jim", LastName="Wooley"
},
}
},
};
var temp = books.SelectMany(book => book.Authors).Distinct();
foreach (var author in temp)
{
Console.WriteLine(author.FirstName + " " + author.LastName);
}
Console.Read();
}
}
public class Book
{
public string Name { get; set; }
public List<Author> Authors { get; set; }
}
public class Author
{
public string FirstName { get; set; }
public string LastName { get; set; }
public override bool Equals(object obj)
{
return true;
//if (obj.GetType() != typeof(Author)) return false;
//else return ((Author)obj).FirstName == this.FirstName && ((Author)obj).FirstName == this.LastName;
}
}
Это основано на примере из «LINQ в действии». Листинг 4.16.
Это напечатает Джона Скита дважды. Зачем? Я даже попытался переопределить метод Equals в классе Author. По-прежнему Distinct не работает. Что мне не хватает?
Изменить: я также добавил перегрузку оператора == и! =. По-прежнему никакой помощи.
public static bool operator ==(Author a, Author b)
{
return true;
}
public static bool operator !=(Author a, Author b)
{
return false;
}
c#
.net
linq
iequatable
iequalitycomparer
Tanmoy
источник
источник
IEquatable
(и переопределилEquals
/GetHashCode
), но ни одна из моих точек останова не срабатывает в этих методах на LinqDistinct
?GetHashCode
иEquals
, они были достигнуты во время цикла foreach. Это связано с тем, чтоvar temp = books.SelectMany(book => book.Authors).Distinct();
возвращаетсяIEnumerable
значение, что означает, что запрос не выполняется сразу, он выполняется только при использовании данных. Если вам нужен пример этого запуска прямо сейчас, добавьте.ToList()
после,.Distinct()
и вы увидите точки останова вEquals
иGetHashCode
перед foreach.Distinct()
Метод проверяет равенство ссылок для ссылочных типов. Это означает, что он ищет буквально один и тот же дублированный объект, а не разные объекты, содержащие одинаковые значения.Существует перегрузка, которая использует IEqualityComparer , поэтому вы можете указать другую логику для определения того, равен ли данный объект другому.
Если вы хотите, чтобы Author обычно вел себя как обычный объект (т. Е. Только эталонное равенство), но для целей Distinct проверять равенство по значениям имен, используйте IEqualityComparer . Если вы всегда хотите, чтобы объекты Author сравнивались на основе значений имен, переопределите GetHashCode и Equals или реализуйте IEquatable .
Двумя членами в
IEqualityComparer
интерфейсе являютсяEquals
иGetHashCode
. Ваша логика для определенияAuthor
равенства двух объектов выглядит так, как если бы строки имени и фамилии совпадали.источник
Другое решение без реализации
IEquatable
,Equals
иGetHashCode
заключается в использовании LINQsGroupBy
метода и для выбора первого элемента из IGrouping.источник
.GroupBy(y => new { y.FirstName, y.LastName })
Есть еще один способ получить отдельные значения из списка пользовательских типов данных:
Конечно, это даст отчетливый набор данных
источник
Distinct()
выполняет сравнение равенства по умолчанию для объектов в перечисляемом. Если вы не переопределилиEquals()
иGetHashCode()
, тогда будет использоваться реализация по умолчанию onobject
, которая сравнивает ссылки.Простое решение - добавить правильную реализацию
Equals()
иGetHashCode()
ко всем классам, которые участвуют в графе объектов, который вы сравниваете (например, Книга и Автор).IEqualityComparer
Интерфейс является удобством , что позволяет реализоватьEquals()
иGetHashCode()
в отдельном классе , когда у вас нет доступа к внутренним классам , которые необходимо сравнить, или если вы используете другой метод сравнения.источник
Вы переопределили Equals (), но не забудьте также переопределить GetHashCode ()
источник
<custom>^base.GetHashCode()
Вышеуказанные ответы неверны !!! Distinct, как указано в MSDN, возвращает Equator по умолчанию, который, как указано, Свойство Default проверяет, реализует ли тип T интерфейс System.IEquatable, и, если да, возвращает EqualityComparer, который использует эту реализацию. В противном случае он возвращает EqualityComparer, который использует переопределения Object.Equals и Object.GetHashCode, предоставленные T
Это означает, что пока вы переигрываете Equals, с вами все в порядке.
Причина, по которой ваш код не работает, заключается в том, что вы проверяете имя == фамилию.
см. https://msdn.microsoft.com/library/bb348436(v=vs.100).aspx и https://msdn.microsoft.com/en-us/library/ms224763(v=vs.100).aspx
источник
Вы можете использовать метод расширения в списке, который проверяет уникальность на основе вычисленного хэша. Вы также можете изменить метод расширения для поддержки IEnumerable.
Пример:
Метод расширения:
источник
Вы можете добиться этого двумя способами:
1. Вы можете реализовать интерфейс IEquatable, как показано в методе Enumerable.Distinct, или вы можете увидеть ответ @skalb в этом посте.
2. Если ваш объект не имеет уникального ключа, вы можете использовать метод GroupBy для создания отдельного списка объектов, который вы должны сгруппировать все свойства объекта и после выбора первого объекта.
Например, как показано ниже, и работает для меня:
Класс MyObject выглядит следующим образом:
3. Если у вашего объекта есть уникальный ключ, вы можете использовать его только в группе.
Например, уникальный ключ моего объекта - Id.
источник