Какова рекомендуемая лучшая практика для использования IEqualityComparer <T>? - PullRequest
10 голосов
/ 16 сентября 2008

Я искал лучшие мировые практики, как другие люди могли реализовывать решения со сложными доменами.

Ответы [ 6 ]

12 голосов
/ 08 октября 2009

Каждый раз, когда вы рассматриваете возможность использования IEqualityComparer<T>, подумайте, можно ли сделать класс для реализации IEquatable<T>. Если Product всегда нужно сравнивать по идентификатору, просто определите его равным как таковому, чтобы вы могли использовать компаратор по умолчанию.

Тем не менее, есть еще несколько причин, по которым вам может понадобиться пользовательский компаратор:

  1. Если существует несколько способов, экземпляры класса можно считать равными. Лучший пример этого - строка, для которой платформа предоставляет шесть различных компараторов в StringComparer.
  2. Если класс определен таким образом, что вы не можете определить его как IEquatable<T>. Это будет включать классы, определенные другими, и классы, сгенерированные компилятором (в частности, анонимные типы, которые по умолчанию используют сравнение свойств).

Если вы решите, что вам нужен компаратор, вы, безусловно, можете использовать обобщенный компаратор (см. Ответ DMenT), но если вам нужно повторно использовать эту логику, вы должны инкапсулировать ее в выделенном классе. Вы даже можете объявить это, унаследовав от общей базы:

class ProductByIdComparer : GenericEqualityComparer<ShopByProduct>
{
    public ProductByIdComparer()
        : base((x, y) => x.ProductId == y.ProductId, z => z.ProductId)
    { }
}

Что касается использования, вы должны по возможности использовать преимущества компараторов. Например, вместо вызова ToLower() для каждой строки, используемой в качестве ключа словаря (логика для которой будет разбросана по всему приложению), вы должны объявить словарь для использования без учета регистра StringComparer. То же самое касается операторов LINQ, которые принимают компаратор. Но опять же, всегда учитывайте, является ли уравновешенное поведение, которое должно быть присуще классу, а не определено внешне.

6 голосов
/ 10 февраля 2009

Я сделал следующее, я не уверен, является ли это лучшей практикой в ​​реальном мире, но у меня она работала нормально. :)

public class GenericEqualityComparer<T> : IEqualityComparer<T>
{
    private Func<T, T, Boolean> _comparer;
    private Func<T, int> _hashCodeEvaluator;
    public GenericEqualityComparer(Func<T, T, Boolean> comparer)
    {
        _comparer = comparer;
    }

    public GenericEqualityComparer(Func<T, T, Boolean> comparer, Func<T, int> hashCodeEvaluator)
    {
        _comparer = comparer;
        _hashCodeEvaluator = hashCodeEvaluator;
    }

    #region IEqualityComparer<T> Members

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

    public int GetHashCode(T obj)
    {
        if(obj == null) {
            throw new ArgumentNullException("obj");
        }
        if(_hashCodeEvaluator == null) {
            return 0;
        } 
        return _hashCodeEvaluator(obj);
    }

    #endregion
}

Тогда вы можете использовать его в своих коллекциях.

var comparer = new GenericEqualityComparer<ShopByProduct>((x, y) => x.ProductId == y.ProductId);
var current = SelectAll().Where(p => p.ShopByGroup == group).ToList();
var toDelete = current.Except(products, comparer);
var toAdd = products.Except(current, comparer);

Если вам требуется поддержка пользовательских функций GetHashCode (), используйте альтернативный конструктор для обеспечения лямбда-выражения для альтернативного вычисления:

var comparer = new GenericEqualityComparer<ShopByProduct>(
       (x, y) => { return x.ProductId == y.ProductId; }, 
       (x)    => { return x.Product.GetHashCode()}
);

Надеюсь, это поможет. =)

5 голосов
/ 13 января 2011

См. Этот пост для (лучших) альтернатив: Оберните делегата в IEqualityComparer

Прокрутите вниз до детали на KeyEqualityComparer и особенно детали на важности GetHashCode . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1.

2 голосов
/ 16 сентября 2008

Вот что MSDN говорит о IEqualityComparer (неуниверсальном):

Этот интерфейс позволяет осуществлять настраиваемое сравнение на равенство для коллекций. То есть вы можете создать собственное определение равенства и указать, что это определение будет использоваться с типом коллекции, который принимает интерфейс IEqualityComparer. В .NET Framework этот интерфейс принимают конструкторы типов коллекций Hashtable, NameValueCollection и OrderedDictionary.

Этот интерфейс поддерживает только сравнения на равенство. Настройка сравнений для сортировки и упорядочения обеспечивается интерфейсом IComparer.

Похоже, что универсальная версия этого интерфейса выполняет ту же функцию, но используется для Dictionary<(Of <(TKey, TValue>)>) коллекций.

Что касается передового опыта использования этого интерфейса в ваших собственных целях. Я бы сказал, что лучше всего использовать его, когда вы создаете или реализуете класс, имеющий функциональность, аналогичную вышеупомянутым коллекциям платформы .NET, и где вы хотите добавить такую ​​же возможность в свои собственные коллекции. Это гарантирует, что вы согласны с тем, как .NET Framework использует интерфейс.

Другими словами, поддерживать использование этого интерфейса, если вы разрабатываете пользовательскую коллекцию и хотите, чтобы ваши потребители могли контролировать равенство, которое используется в ряде методов LINQ и связанных с коллекцией (например, Sort).

1 голос
/ 16 сентября 2008

Список использует этот интерфейс много, так что вы можете сказать a.Substract (b) или другие из этих приятных функций.

Просто помните: если ваши объекты не возвращают один и тот же хэш-код, Equals не вызывается.

1 голос
/ 16 сентября 2008

Я бы сказал, что лучше всего будет использовать разные правила равенства для определенного алгоритма. Почти так же, как алгоритм сортировки может принять IComparer<T>, алгоритм поиска может принять IEqualityComparer<T>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...