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;
}
}
Jest to oparte na przykładzie w „LINQ w akcji”. Listing 4.16.
To drukuje Jon Skeet dwukrotnie. Czemu? Próbowałem nawet przesłonić metodę Equals w klasie Author. Wciąż Odrębny nie wydaje się działać. czego mi brakuje?
Edycja: dodałem też przeciążenie operatorów == i! =. Wciąż bez pomocy.
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
źródło
źródło
IEquatable
(i overrodeEquals
/GetHashCode
), ale żaden z moich punktów przerwania nie działa w tych metodach na LinqDistinct
?GetHashCode
iEquals
, zostały trafione podczas pętli foreach. Dzieje się tak, ponieważvar temp = books.SelectMany(book => book.Authors).Distinct();
zwraca anIEnumerable
, co oznacza, że żądanie nie jest wykonywane od razu, jest wykonywane tylko wtedy, gdy dane są używane. Jeśli chciałbyś od razu zobaczyć przykład tego odpalenia, dodaj.ToList()
po znaku, a.Distinct()
zobaczysz punkty przerwania wEquals
iGetHashCode
przed foreach.Distinct()
Równość sprawdza metody referencyjnej dla typów referencyjnych. Oznacza to, że szuka dosłownie tego samego zduplikowanego obiektu, a nie różnych obiektów, które zawierają te same wartości.Istnieje przeciążenie, które pobiera IEqualityComparer , więc można określić inną logikę do określania, czy dany obiekt jest równy innemu.
Jeśli chcesz, aby Autor zachowywał się normalnie jak normalny obiekt (tj. Tylko równość odwołań), ale dla celów Distinct sprawdzaj równość według wartości nazw, użyj IEqualityComparer . Jeśli zawsze chcesz, aby obiekty autora były porównywane na podstawie wartości nazw, zastąp GetHashCode i Equals lub zaimplementuj IEquatable .
Dwa elementy
IEqualityComparer
interfejsu toEquals
iGetHashCode
. Twoja logika określania, czy dwaAuthor
obiekty są równe, wygląda na to, że ciągi Imię i Nazwisko są takie same.źródło
Innym rozwiązaniem bez realizacji
IEquatable
,Equals
iGetHashCode
jest użycie LINQsGroupBy
metody i wybierz pierwszą pozycję z IGrouping.źródło
.GroupBy(y => new { y.FirstName, y.LastName })
Istnieje jeszcze jeden sposób uzyskania odrębnych wartości z listy typów danych zdefiniowanych przez użytkownika:
Z pewnością da inny zestaw danych
źródło
Distinct()
wykonuje domyślne porównanie równości obiektów w enumerable. Jeśli nie przesłoniłeśEquals()
iGetHashCode()
, to używa domyślnej implementacji onobject
, która porównuje odwołania.Prostym rozwiązaniem jest dodanie prawidłowej realizacji
Equals()
iGetHashCode()
do wszystkich klas, które uczestniczą w wykresie obiektu porównujesz (tj książkę i autora).IEqualityComparer
Interfejs jest wygoda, która pozwala na wdrożenieEquals()
iGetHashCode()
w oddzielnej klasie, gdy nie masz dostępu do wewnętrznych części zajęć trzeba porównać, lub jeśli używasz innej metody porównywania.źródło
Zastąpiłeś Equals (), ale upewnij się, że nadpisałeś również GetHashCode ()
źródło
<custom>^base.GetHashCode()
Powyższe odpowiedzi są błędne !!! Distinct, jak podano w MSDN, zwraca domyślny Equator, który zgodnie z opisem Właściwość Default sprawdza, czy typ T implementuje interfejs System.IEquatable, a jeśli tak, zwraca EqualityComparer, który używa tej implementacji. W przeciwnym razie zwraca EqualityComparer, który używa zastąpień Object.Equals i Object.GetHashCode dostarczone przez T
Co oznacza, że dopóki przewyższasz Równe, nic ci nie jest.
Kod nie działa, ponieważ sprawdzasz firstname == lastname.
patrz https://msdn.microsoft.com/library/bb348436(v=vs.100).aspx i https://msdn.microsoft.com/en-us/library/ms224763(v=vs.100).aspx
źródło
Możesz użyć metody rozszerzenia na liście, która sprawdza unikalność na podstawie obliczonego skrótu. Możesz również zmienić metodę rozszerzenia, aby obsługiwała IEnumerable.
Przykład:
Metoda rozszerzenia:
źródło
Możesz to osiągnąć na dwa sposoby:
1. Możesz zaimplementować interfejs IEquatable zgodnie z metodą Enumerable.Distinct lub możesz zobaczyć odpowiedź @ skalb w tym poście
2. Jeśli twój obiekt nie ma unikalnego klucza, możesz użyć metody GroupBy dla uzyskania listy odrębnych obiektów, musisz zgrupować wszystkie właściwości obiektu, a następnie wybrać pierwszy obiekt.
Na przykład jak poniżej i pracuje dla mnie:
Klasa MyObject wygląda następująco:
3. Jeśli twój obiekt ma unikalny klucz, możesz go używać tylko w grupie.
Na przykład unikalnym kluczem mojego obiektu jest Id.
źródło