Что Collection.Contains() использует для проверки существующих объектов?

У меня есть строго типизированный список пользовательских объектов, MyObject, у которого есть свойство Id наряду с некоторыми другими свойствами.
Скажем, Id объекта MyObject определяет его как уникальный, и я хочу проверить, нет ли в моей коллекции объекта MyObject с идентификатором 1, прежде чем я добавлю свой новый MyObject в коллекцию.
Я хочу использовать if(!List.Contains(myObj)), но как мне обеспечить тот факт, что только одно или два свойства MyObject определяют его как уникальное?
Я могу использовать IComparable? Или мне нужно только переопределить метод Equals, но сначала мне нужно что-то унаследовать, верно?



Спасибо

6 ответов

Решение

List<T>.Contains использования EqualityComparer<T>.Defaultкоторый в свою очередь использует IEquatable<T> если тип реализует это, или object.Equals иначе.

Вы могли бы просто реализовать IEquatable<T> но это хорошая идея переопределить object.Equals если вы сделаете это, и очень хорошая идея переопределить GetHashCode() если вы сделаете это:

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

Обратите внимание, что хеш-код относится к критериям равенства. Это жизненно важно.

Это также делает его применимым для любого другого случая, когда полезно равенство, как определено тем же идентификатором. Если у вас есть одно требование, чтобы проверить, есть ли в списке такой объект, то я бы, вероятно, предложил просто сделать:

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

List<T> использует компаратор, возвращаемый EqualityComparer<T>.Default и согласно документации для этого:

Свойство Default проверяет, реализует ли тип T интерфейс System.IEquatable(Of T) и, если да, возвращает EqualityComparer(Of T), который использует эту реализацию. В противном случае он возвращает EqualityComparer(Of T), который использует переопределения Object.Equals и Object.GetHashCode, предоставленные T.

Таким образом, вы можете реализовать IEquatable<T> в вашем пользовательском классе или переопределите Equals (а также GetHashCode) методы для сравнения по требуемым свойствам. В качестве альтернативы вы можете использовать linq:

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

Вы можете использовать LINQ, чтобы сделать это довольно легко.

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

Вы можете переопределить Equals и GetHashCode, реализовать IEqualityComparer<MyObject> и использовать это в Contains вызовите или используйте метод расширения, например Any

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

Сначала определите вспомогательный класс с 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);
    }
}

Затем в вашем коде просто определите компаратор и используйте его:

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

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

Таким образом, вы можете определить способ вычисления равенства без изменения ваших классов. Вы также можете использовать его для обработки объектов из сторонних библиотек, поскольку вы не можете изменять их код.

Ты можешь использовать IEquatable<T>, Реализуйте это в своем классе, а затем проверьте, имеет ли T, переданный в Equals, тот же Id, что и this.Id. Я уверен, что это работает для проверки ключа в словаре, но я не использовал его для коллекции.

Другие вопросы по тегам