2016-10-04 38 views
7

Używam metody Enumerable.Union<TSource> do uzyskania unii listy niestandardowej1 z listą niestandardową2. Ale jakoś to nie działa tak, jak powinno w moim przypadku. Otrzymuję wszystkie przedmioty także duplikat jeden raz.Jak używać C# LINQ Union, aby uzyskać Unię niestandardowej listy1 z listą2

Poszedłem za MSDN Link, aby wykonać pracę, ale nadal nie jestem w stanie osiągnąć tego samego.

Poniżej przedstawiono kod klasy niestandardowego: -

public class CustomFormat : IEqualityComparer<CustomFormat> 
{ 
    private string mask; 

    public string Mask 
    { 
     get { return mask; } 
     set { mask = value; } 
    } 

    private int type;//0 for Default 1 for userdefined 

    public int Type 
    { 
     get { return type; } 
     set { type = value; } 
    } 
    public CustomFormat(string c_maskin, int c_type) 
    { 
     mask = c_maskin; 
     type = c_type; 
    } 

    public bool Equals(CustomFormat x, CustomFormat y) 
    { 
     if (ReferenceEquals(x, y)) return true; 

     //Check whether the products' properties are equal. 
     return x != null && y != null && x.Mask.Equals(y.Mask) && x.Type.Equals(y.Type); 
    } 

    public int GetHashCode(CustomFormat obj) 
    { 
     //Get hash code for the Name field if it is not null. 
     int hashProductName = obj.Mask == null ? 0 : obj.Mask.GetHashCode(); 

     //Get hash code for the Code field. 
     int hashProductCode = obj.Type.GetHashCode(); 

     //Calculate the hash code for the product. 
     return hashProductName^hashProductCode; 
    } 
} 

Ten Wołam w następujący sposób: -

List<CustomFormat> l1 = new List<CustomFormat>(); 
l1.Add(new CustomFormat("#",1)); 
l1.Add(new CustomFormat("##",1)); 
l1.Add(new CustomFormat("###",1)); 
l1.Add(new CustomFormat("####",1)); 

List<CustomFormat> l2 = new List<CustomFormat>(); 
l2.Add(new CustomFormat("#",1)); 
l2.Add(new CustomFormat("##",1)); 
l2.Add(new CustomFormat("###",1)); 
l2.Add(new CustomFormat("####",1)); 
l2.Add(new CustomFormat("## ###.0",1)); 

l1 = l1.Union(l2).ToList(); 

foreach(var l3 in l1) 
{ 
    Console.WriteLine(l3.Mask + " " + l3.Type); 
} 

Proszę zaproponować odpowiedni sposób, aby osiągnąć to samo!

+3

Wydaje się dziwne, ale twój kod działa, jeśli a) dostarczasz konstruktorowi bez parametrów dla CustomFormat i przekazujesz instancję tej klasy do metody Union - patrz https://dotnetfiddle.net/YTVwTI. Powstaje pytanie, dlaczego Unia ignoruje implementację klasy IEqualityComparer . – stuartd

Odpowiedz

8

Osobliwością tutaj jest to, że Twoja klasa implementuje IEqualityComparer<CustomClass> zamiast IEquatable<CustomClass>. Możesz może przejść w innej instancji CustomClass, który byłby używany jako porównywarka, ale byłoby bardziej idiomatyczne, aby po prostu dokonać CustomClass realizacji IEquatable<CustomClass>, a także zastąpić Equals(object).

Różnica między IEquatable<T> i IEqualityComparer<T> że IEquatable<T> mówi: „Wiem, jak porównywać się z inną instancję T” natomiast IEqualityComparer<T> mówi „wiem, jak porównać dwa przypadki T”. Ta ostatnia jest zwykle dostarczana osobno - tak jak może być dostarczona do Union za pośrednictwem innego parametru. Bardzo rzadko zdarza się, że typ implementuje IEqualityComparer<T> dla swojego własnego typu - podczas gdy IEquatable<T> powinien być używany tylko do porównywania wartości tego samego typu.

Oto implementacja wykorzystująca automatycznie zaimplementowane właściwości dla uproszczenia i bardziej idiomatyczne nazwy parametrów. Prawdopodobnie sam wymieniłbym implementację kodu skrótu i ​​używałbym członkostw ekspresyjnych, ale to już inna sprawa.

public class CustomFormat : IEquatable<CustomFormat> 
{ 
    public string Mask { get; set; } 
    public int Type { get; set; } 

    public CustomFormat(string mask, int type) 
    { 
     Mask = mask; 
     Type = type; 
    } 

    public bool Equals(CustomFormat other) 
    { 
     if (ReferenceEquals(this, other)) 
     { 
      return true; 
     } 
     return other != null && other.Mask == Mask && other.Type == Type; 
    } 

    public override bool Equals(object obj) 
    { 
     return Equals(obj as CustomFormat); 
    } 

    public override int GetHashCode() 
    { 
     // Get hash code for the Name field if it is not null. 
     int hashProductName = Mask == null ? 0 : Mask.GetHashCode(); 

     //Get hash code for the Code field. 
     int hashProductCode = Type.GetHashCode(); 

     //Calculate the hash code for the product. 
     return hashProductName^hashProductCode; 
    } 
} 

Teraz to nie pomaga, że ​​(jak zauważył w komentarzach) dokumentację dla Enumerable.Union jest źle. Obecnie stwierdza:

Domyślna comparer równość, Default, używana jest do porównywania wartości typów, które implementują interfejs rodzajowe IEqualityComparer<T>.

Należy powiedzieć coś takiego:

Domyślna comparer równość, Default, służy do porównywania wartości, gdy określony IEqualityComparer<T> nie jest przewidziane. Jeśli T implementuje IEquatable<T>, domyślny porównawca użyje tej implementacji. W przeciwnym razie użyje implementacji Equals(object).

+0

Próbowałem implementacji, ale nie wydaje się, aby rozwiązać problem. Zobacz kod [link] (https://dotnetfiddle.net/YTVwTI). Nie pokazuje błędu, ale pokazane są duplikaty przedmiotów. – JDoshi

+0

@JDoshi: To skrzypce nadal implementuje 'IEqualityComparer '. Podany przeze mnie kod (implementacja 'IEquatable ' *) * działa z podanym przykładem. –

+0

Właściwie to edytowałem w tym samym skrzypcach i udostępniłem ci link, który został ci udostępniony przez stuartd. Więc przypadkowo podzieliłeś się niewłaściwym skrzypce. Przepraszamy za błąd :( – JDoshi

3

Należy przekazać instancję obiektu IEqualityComparer do metody Union. Metoda ma przeciążenie, które należy przekazać w porówna- niu.

Najprostszym rozwiązaniem jest najbrzydszy i

var comparer = new CustomFormat(null,0); 

l1 = l1.Union(l2, comparer).ToList(); 

Dałeś jakieś błędy w implementacji. Nie powinieneś implementować metody IEqualityComparer na swoim typie (CustomFormat), ale na osobnej klasie, takiej jak CustomFormatComparer.

Na swoim typie (CustomFormat) należy zastosować IEquatable.

+4

Łącze MSDN dla Unii wprowadza w błąd, tak jak mówi _ "Domyślny porównywanie równości, Domyślnie, służy do porównywania wartości typów implementujących ogólny interfejs IEqualityComparer . ** Aby porównać niestandardowy typ danych, potrzebujesz aby zaimplementować ten interfejs i dostarczyć własne metody GetHashCode i Equals dla typu **. "_ nie zakłada, że ​​musi to być osobna klasa. – stuartd

+0

@Jehof Czy możesz wyjaśnić użycie 'IEquatable' w tym przypadku. W tym przypadku implementacja 'IEquatable' bezpośrednio, a nie' IEqualityComparer', powoduje błędy od czasu użycia 'Union'. – JDoshi

+0

@JDoshi: Nie, nie - implementacja 'IEquatable ' naprawdę jest drogą do przodu tutaj. –