2010-09-03 19 views
35

Tengo una lista fuertemente tipada de objetos personalizados, MyObject, que tiene un Id. De propiedad junto con algunas otras propiedades.
Digamos que el Id de un MyObject lo define como único y quiero verificar si mi colección aún no tiene un objeto MyObject que tenga un Id de 1 antes de agregar mi nuevo MyObject a la colección.
Quiero usar if (! List.Contains (myObj)) pero, ¿cómo puedo hacer que el hecho de que solo una o dos propiedades de MyObject lo definan como único?
Puedo usar IComparable? ¿O solo tengo que anular el método Equals pero necesitaría heredar algo primero? ¿Es así?¿Qué usa Collection.Contains() para verificar si hay objetos existentes?



Gracias

Respuesta

36

List<T>.Contains utiliza EqualityComparer<T>.Default, que a su vez utiliza IEquatable<T> si el tipo lo implementa, o object.Equals lo contrario.

Se podía aplicar IEquatable<T> pero es una buena idea para anular object.Equals si lo hace, y una muy buena idea para anular GetHashCode() si lo hace:

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; 
    } 
} 

Tenga en cuenta que el código hash se relaciona a los criterios de igualdad. Esto es vital.

Esto también lo hace aplicable para cualquier otro caso donde la igualdad, definida por tener la misma ID, es útil.Si usted tiene un uno-de requerimiento para comprobar si una lista tiene un objeto así, entonces probablemente me propongo simplemente haciendo:

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

Se puede utilizar LINQ para hacer esto con bastante facilidad.

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

oh, derecho. eso parece estar bien – topwik

+5

Un poco más conciso sería: MyCollection.Any (x => x.myId == Id) –

+8

No solo es más conciso, sino que debería iterar sobre toda la colección. 'Any' sería un circuito corto en el primer partido. –

0

Puede usar IEquatable<T>. Implemente esto en su clase, y luego verifique si el T pasado a Equals tiene el mismo ID que este. Id. Estoy seguro de que esto funciona para verificar una clave en un diccionario, pero no la he usado para una colección.

2

Puede anular iguales y GetHashCode, implementar un IEqualityComparer<MyObject> y el uso que en la llamada Contains, o usar un método de extensión como Any

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

+1, esta es una buena solución de alto rendimiento, especialmente si usa conjuntos o diccionarios. (Hablando al punto de Equals/GetHashCode) –

27

List<T> utiliza el comparador devuelto por EqualityComparer<T>.Default y según la documentation para ese :

cheques propiedad

el valor por defecto si tipo T implementa el System.IEquatable (De T) y la interfaz, si es así, devuelve un EqualityComparer (De T) que utiliza esa implementación. De lo contrario, devuelve un EqualityComparer (Of T) que utiliza los anulaciones de Object.equals y Object.GetHashCode provistos por T.

por lo que puede poner en práctica IEquatable<T> en su clase personalizada, o anular el Equals (y GetHashCode) métodos para hacer la comparación por las propiedades que necesita. Otra posibilidad es utilizar LINQ:

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

Me gustaría ir con la declaración de Linq. Si lo desea, podría implementar un método de extensión Contiene (lista de esta lista , elemento T, comparador Func ) que se parecería a List.Contains pero agregue la capacidad de proporcionar un delegado de comparación. Envolvería una declaración de Linq similar a la proporcionada aquí. – KeithS

+0

+1 para el linq - Estaba buscando este fragmento de código exacto – elwyn

+0

+1 para el LINQ también, porque le permite comparar clases de fuentes externas. Y por código de elegancia – PPC

0

Primera clase de ayuda a definir con 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); 
    } 
} 

Luego, en el código, simplemente definir comparador y utilizarla:

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

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

De esta manera se puede definir la forma en cómo se calcula la igualdad sin modificar sus clases. También puede usarlo para procesar objetos de bibliotecas de terceros ya que no puede modificar su código.

Cuestiones relacionadas