2009-10-25 16 views
8

Mam następujących klas:LinQ odrębny z niestandardowym comparer pozostawia duplikaty

public class SupplierCategory : IEquatable<SupplierCategory> 
{ 
    public string Name { get; set; } 
    public string Parent { get; set; } 

    #region IEquatable<SupplierCategory> Members 

    public bool Equals(SupplierCategory other) 
    { 
     return this.Name == other.Name && this.Parent == other.Parent; 
    } 

    #endregion 
} 

public class CategoryPathComparer : IEqualityComparer<List<SupplierCategory>> 
{ 
    #region IEqualityComparer<List<SupplierCategory>> Members 

    public bool Equals(List<SupplierCategory> x, List<SupplierCategory> y) 
    { 
     return x.SequenceEqual(y); 
    } 

    public int GetHashCode(List<SupplierCategory> obj) 
    { 
     return obj.GetHashCode(); 
    } 

    #endregion 
} 

i używam następujące kwerendy LINQ:

CategoryPathComparer comparer = new CategoryPathComparer(); 
List<List<SupplierCategory>> categoryPaths = (from i in infoList 
                  select 
                  new List<SupplierCategory>() { 
                  new SupplierCategory() { Name = i[3] }, 
                  new SupplierCategory() { Name = i[4], Parent = i[3] }, 
                  new SupplierCategory() { Name = i[5], Parent = i[4] }}).Distinct(comparer).ToList(); 

Ale odrębne nie robić to, co ja chcesz to zrobić, jak pokazuje poniższy kod:

comp.Equals(categoryPaths[0], categoryPaths[1]); //returns True 

Czy używam tego w niewłaściwy sposób? dlaczego nie są porównywane, tak jak zamierzam?

Edit: Aby wykazać The comparer działa następujące zwraca true, jak powinien:

List<SupplierCategory> list1 = new List<SupplierCategory>() { 
    new SupplierCategory() { Name = "Cat1" }, 
    new SupplierCategory() { Name = "Cat2", Parent = "Cat1" }, 
    new SupplierCategory() { Name = "Cat3", Parent = "Cat2" } 
}; 
List<SupplierCategory> list1 = new List<SupplierCategory>() { 
    new SupplierCategory() { Name = "Cat1" }, 
    new SupplierCategory() { Name = "Cat2", Parent = "Cat1" }, 
    new SupplierCategory() { Name = "Cat3", Parent = "Cat2" } 
}; 
CategoryPathComparer comp = new CategoryPathComparer(); 
Console.WriteLine(comp.Equals(list1, list2).ToString()); 
+0

Retag. Nie ma C# 3.5 (zobacz http://stackoverflow.com/questions/247621/what-are-the-correct-version-numbers-for-c) – Vaccano

Odpowiedz

10

Twoim problemem jest to, że nie wdrożył IEqualityComparer poprawnie.

Po wdrożeniu IEqualityComparer<T> musisz musi zaimplementować GetHashCode, aby dwa równe obiekty miały ten sam kod skrótu.

W przeciwnym razie pojawi się nieprawidłowe zachowanie, jak widać tutaj.

Należy wdrożyć GetHashCode w następujący sposób: (dzięki uprzejmości this answer)

public int GetHashCode(List<SupplierCategory> obj) { 
    int hash = 17; 

    foreach(var value in obj) 
     hash = hash * 23 + obj.GetHashCode(); 

    return hash; 
} 

Należy również zastąpić GetHashCode w SupplierCategory być spójne. Na przykład:

public override int GetHashCode() { 
    int hash = 17; 
    hash = hash * 23 + Name.GetHashCode(); 
    hash = hash * 23 + Parent.GetHashCode(); 
    return hash; 
} 

Wreszcie, choć nie muszą, powinieneś zastąpić Equals w SupplierCategory i uczynić go wywołać metodę Equals Państwo wdrożony IEquatable.

4

W rzeczywistości ten problem jest nawet objęty dokumentacją: http://msdn.microsoft.com/en-us/library/bb338049.aspx.

+0

Masz na myśli zawartość społeczności? – SLaks

+0

Nie, mam na myśli, że dokumentacja zawiera przykłady kodu na VB i C#, które pokazują, jak stworzyć własny porównywalnik. Pokazuje również, w jaki sposób można zastąpić metody GetHashCode i Equals. –