2011-08-12 11 views
17

Próbuję znaleźć różnicę między dwiema ogólnymi listami, tak jak w poniższym przykładzie. Chociaż t1 i t2 zawierają te same właściwości, nie są tym samym obiektem, więc muszę zaimplementować IEqualityComparer.Linq Z wyjątkiem niestandardowego produktu IEqualityComparer

Wygląda na to, że działa z tym przykładem, ale prawdziwa klasa ma kilka innych właściwości i muszę zrobić to samo z kilkoma innymi klasami.

Więc zastanawiałem się, czy jestem ponownie wynalazca koła?

Czy istnieje łatwiejsza metoda porównywania wszystkich właściwości dwóch obiektów? W tej chwili, naprawdę potrzebuję tylko radzić sobie z klasy zawierające proste typy, ale byłoby miło, że mam porównywarkę, która pracowała z klasami, które zawierają wystąpienia innych klas.

void Main() 
{ 
    var t1 = new Sizes { Name = "Test" , Size = 1} ; 
    var t2 = new Sizes { Name = "Test" , Size = 1} ; 

    var list1 = new List<Sizes>(); 
    var list2 = new List<Sizes>(); 
    list1.Add(t1); 
    list2.Add(t2); 

    var differences = list2.Except(list1 , new SizesComparer());  
    // differences should be empty. 
} 


public class Sizes 
{ 
    public string Name { get; set; } 
    public int Size { get; set; } 
} 

public class SizesComparer : IEqualityComparer<Sizes> 
{ 
    bool IEqualityComparer<Sizes>.Equals(Sizes x, Sizes y) 
    {    
     return (x.Name.Equals(y.Name) && x.Size.Equals(y.Size));   
    } 

    int IEqualityComparer<Sizes>.GetHashCode(Sizes obj) 
    { 
     if (Object.ReferenceEquals(obj, null)) 
      return 0;    

     return obj.Name.GetHashCode() + obj.Size;  
    } 
} 
+1

See IEnumerable.Except() i niestandardowych comparer - http://stackoverflow.com/q/1299513/485076 – sll

Odpowiedz

4

rozwiązanie, które skończyło się za pomocą nie można opisać jako szybka, ale to nie jest problemem kopalni i robi to, co chcę, że może być ponownie użyte i nie jest ograniczone do żadnej konkretnej klasy.

Używa biblioteki Newtonsoft.Json do serializacji obiektu do łańcucha, a następnie porównuje wynik. Ma to tę zaletę, że działa z klasami anonimowymi i klasami zagnieżdżonymi.

Zakładam, że sposób, w jaki działa porównanie, polega na tym, że najpierw wywołuje on kod GetHashCode na obu obiektach i jeśli pasują do siebie, wywołuje wartości równe, co w tej procedurze oznacza, że ​​pasujące obiekty będą serializowane dwukrotnie.

public class JSonEqualityComparer<T> : IEqualityComparer<T> 
{ 
    public bool Equals(T x, T y) 
    {   
     return String.Equals 
     ( 
      Newtonsoft.Json.JsonConvert.SerializeObject(x), 
      Newtonsoft.Json.JsonConvert.SerializeObject(y) 
     );     
    } 

    public int GetHashCode(T obj) 
    {       
     return Newtonsoft.Json.JsonConvert.SerializeObject(obj).GetHashCode();   
    }    
}  


public static partial class LinqExtensions 
{ 
    public static IEnumerable<T> ExceptUsingJSonCompare<T> 
     (this IEnumerable<T> first, IEnumerable<T> second) 
    { 
     return first.Except(second, new JSonEqualityComparer<T>()); 
    } 
} 

Aby używać go zamienić z wyjątkiem ExceptUsingJSonCompare, na przykład:

var differences = list2.ExceptUsingJSonCompare(list1); 
0

Najlepszy sposób Zazwyczaj na to spróbować mieć unikalny klucz/composite w każdym objectand tamtędy tylko porównanie kluczową rolę i nie mam porównania innych bitów.

Możesz użyć Odbicia, które przejdzie przez każdą publiczną właściwość klasy (która będzie bardzo ogólna) i porówna je, ale będzie wolniejsze.

32

można spróbować coś takiego:

var differences = list2.Where(l2 => 
    !list1.Any(l1 => l1.Name == l2.Name && l1.Size == l2.Size)); 
+0

Działa to doskonale, jeśli dodajesz obiekty do list tego samego typu. –

+1

Dzięki. O wiele bardziej zwięzły niż przy użyciu IEqualityComparer – garyh

Powiązane problemy