2010-09-03 15 views
35

Mam silnie wpisaną listę niestandardowych obiektów, MyObject, który ma identyfikator nieruchomości wraz z innymi właściwościami.
Załóżmy, że identyfikator obiektu MyObject definiuje go jako unikalny i chcę sprawdzić, czy moja kolekcja nie ma już obiektu MyObject, który ma identyfikator 1, zanim doda mój nowy obiekt MyObject do kolekcji.
Chcę użyć, jeśli (! List.Contains (myObj)), ale w jaki sposób wymusić fakt, że tylko jedna lub dwie właściwości MyObject zdefiniować go jako unikatowy?
Mogę używać IComparable? Czy muszę tylko zastąpić metodę Equals, ale muszę najpierw coś odziedziczyć, prawda?Co służy funkcja Collection.Contains() do sprawdzania istniejących obiektów?



Dzięki

Odpowiedz

36

List<T>.Contains wykorzystuje EqualityComparer<T>.Default, który z kolei używa IEquatable<T> jeśli typ implementuje go lub object.Equals inaczej.

Można po prostu wdrożyć IEquatable<T> ale jest to dobry pomysł, aby zastąpić object.Equals jeśli to zrobisz, a bardzo dobry pomysł, aby zastąpić GetHashCode() jeśli zrobić:

public class SomeIDdClass : IEquatable<SomeIDdClass> 
{ 
    private readonly int _id; 
    public SomeIDdClass(int id) 
    { 
     _id = id; 
    } 
    public int Id 
    { 
     get { return _id; } 
    } 
    public bool Equals(SomeIDdClass other) 
    { 
     return null != other && _id == other._id; 
    } 
    public override bool Equals(object obj) 
    { 
     return Equals(obj as SomeIDdClass); 
    } 
    public override int GetHashCode() 
    { 
     return _id; 
    } 
} 

Należy pamiętać, że kod skrótu dotyczy do kryteriów równości. Jest to niezbędne.

Umożliwia to również zastosowanie w każdym innym przypadku, w którym równość, zgodnie z definicją o tym samym identyfikatorze, jest przydatna.Jeśli masz jeden z wymogiem, by sprawdzić, czy lista ma takiego obiektu, a potem pewnie Proponuję po prostu robi:

return someList.Any(item => item.Id == cmpItem.Id); 
4

Można użyć LINQ to zrobić dość łatwo.

var result = MyCollection.Any(p=>p.myId == Id); 
if(result) 
{ 
    //something 
} 
+0

o tak dalej. to wydaje się w porządku. – topwik

+5

Nieco bardziej zwięzły byłby: MyCollection.Any (x => x.myId == Id) –

+8

Nie tylko bardziej zwięzły, ale musiałby powtórzyć cały zbiór. "Any" miałoby krążyć wokół pierwszego meczu. –

0

Można użyć IEquatable<T>. Wdróż to w swojej klasie, a następnie sprawdź, czy T przekazany do Equals ma ten sam identyfikator co ID. Na pewno działa to w przypadku sprawdzania klucza w słowniku, ale nie użyłem go do kolekcji.

2

można przesłonić równa się i GetHashCode, wdrożenie IEqualityComparer<MyObject> i użyć jej w wezwaniu Contains lub użyć metodę rozszerzenia jak Any

if (!myList.Any(obj => obj.Property == obj2.Property && obj.Property2 == obj2.Property2)) 
    myList.Add(obj2); 
+0

+1, jest to dobre rozwiązanie o wysokiej wydajności, szczególnie jeśli używasz zestawów lub słowników. (zwracając się do punktu Equals/GetHashCode) –

27

List<T> używa porównywarka zwrócony przez EqualityComparer<T>.Default i zgodnie z documentation za to :

domyślnej sprawdza własności czy typu T implementuje System.IEquatable (Of T) i, , jeśli tak, zwraca EqualityComparer (Of T), który używa tej implementacji. W przeciwnym razie, zwraca EqualityComparer (T), który używa przesłonięcia z Object.Equals i Object.GetHashCode dostarczone przez T.

Więc może albo wdrożyć IEquatable<T> na niestandardowej klasy lub przesłonić Equals (i GetHashCode) metody porównywania według właściwości, które są wymagane. Alternatywnie można użyć LINQ:

bool contains = list.Any(i => i.Id == obj.Id); 
+0

Poszedłbym z oświadczeniem Linq. Jeśli chcesz, możesz zaimplementować metodę rozszerzenia Zawiera (lista Listę , element T, porównanie Func ), która wyglądałaby jako List.Contains, ale dodała możliwość przekazania delegata porównania. Zawierałoby to instrukcję Linq podobną do tej, która została tu podana. – KeithS

+0

+1 dla linq - szukałem dokładnie tego fragmentu kodu – elwyn

+0

+1 również dla LINQ, ponieważ pozwala on porównywać klasy z zewnętrznych źródeł. I dla elegancji kodu – PPC

0

pierwsze zdefiniować klasę pomocniczą z IEqualityComparer.

public class MyEqualityComparer<T> : IEqualityComparer<T> 
{ 
    Func<T, int> _hashDelegate; 

    public MyEqualityComparer(Func<T, int> hashDelegate) 
    { 
     _hashDelegate = hashDelegate; 
    } 

    public bool Equals(T x, T y) 
    { 
     return _hashDelegate(x) == _hashDelegate(y); 
    } 

    public int GetHashCode(T obj) 
    { 
     return _hashDelegate(obj); 
    } 
} 

Następnie w kodzie, wystarczy zdefiniować komparator i używać go:

var myComparer = new MyEqualityComparer<MyObject>(delegate(MyObject obj){ 
    return obj.ID; 
}); 

var result = collection 
    .Where(f => anotherCollection.Contains(f.First, myComparer)) 
    .ToArray(); 

W ten sposób można określić sposób, w jaki równość jest obliczana bez modyfikowania swoich klas. Można go również użyć do przetwarzania obiektów z bibliotek stron trzecich, ponieważ nie można modyfikować ich kodu.

Powiązane problemy