Как использовать IEqualityComparer


У меня есть несколько колокольчиков в моей базе данных с тем же номером. Я хочу получить их все без дублирования. Затем я создаю класс сравнения для выполнения этой работы, но выполнение функции делает большую задержку от функции без отличия, от 0,6 сек до 3,2 сек!

Я делаю это правильно или я должен использовать другой метод?

       reg.AddRange((from a in this.dataContext.reglements
                     join b in this.dataContext.Clients on a.Id_client equals b.Id
                     where a.date_v <= datefin && a.date_v >= datedeb
                     where a.Id_client == b.Id
                     orderby a.date_v descending 
                     select new Class_reglement
                     {
                         nom = b.Nom,
                         code = b.code,
                         Numf = a.Numf,
                     }).AsEnumerable().Distinct(new Compare()).ToList());


    class Compare : IEqualityComparer<Class_reglement>
    {
        public bool Equals(Class_reglement x, Class_reglement y)
        {
            if (x.Numf == y.Numf)
            {
                return true;
            }
            else { return false; }
        }
        public int GetHashCode(Class_reglement codeh)
        {
            return 0;
        }

    }
6 76

6 ответов:

неудивительно, учитывая ваш GetHashCode реализация, которая всегда возвращает одно и то же значение. Distinct полагается на хорошую хэш-функцию для эффективной работы.

при реализации интерфейсов классов вы должны прочитать их документация первый, в противном случае вы не знаете, какой контракт вы должны реализовать.1

в вашем коде решение состоит в том, чтобы переслать GetHashCode to Class_reglement.Numf.GetHashCode и реализовать его соответственно там.

кроме того, ваш Equals метод полон ненужного кода. Он может быть переписан следующим образом (та же семантика, ¼ кода, более читаемый):

public bool Equals(Class_reglement x, Class_reglement y)
{
    return x.Numf == y.Numf;
}

кроме того,ToList вызов является ненужным и трудоемким: AddRange принимает IEnumerable преобразование к List не требуется. AsEnumerable и и избыточно здесь с момента обработки результата в AddRange приведет это в любом случае.


1 реализация кода, не зная, что он на самом деле делает называется культ карго программирования. Это удивительно распространенная практика. Это принципиально не работает.

попробуйте этот код:

public class GenericCompare<T> : IEqualityComparer<T> where T : class
{
    private Func<T, object> _expr { get; set; }
    public GenericCompare(Func<T, object> expr)
    {
        this._expr = expr;
    }
    public bool Equals(T x, T y)
    {
        var first = _expr.Invoke(x);
        var sec = _expr.Invoke(y);
        if (first != null && first.Equals(sec))
            return true;
        else
            return false;
    }
    public int GetHashCode(T obj)
    {
        return obj.GetHashCode();
    }
}

пример его использования будет

collection = collection
    .Except(ExistedDataEles, new GenericCompare<DataEle>(x=>x.Id))
    .ToList(); 

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

вы должны попробуйте вместо этого разработать предложение where, удовлетворяющее вашим требованиям, см. используя интерфейс IEqualityComparer с LINQ к объектам, за исключением пункта для более подробной информации.

просто код, с выполнения GetHashCode и NULL проверка:

public class Class_reglementComparer : IEqualityComparer<Class_reglement>
{
    public bool Equals(Class_reglement x, Class_reglement y)
    {
        if (x is null || y is null))
            return false;

        return x.Numf == y.Numf;
    }

    public int GetHashCode(Class_reglement product)
    {
        //Check whether the object is null 
        if (product is null) return 0;

        //Get hash code for the Numf field if it is not null. 
        int hashNumf = product.hashNumf == null ? 0 : product.hashNumf.GetHashCode();

        return hashNumf;
    }
}

пример: список Class_reglement отдельные Numf

List<Class_reglement> items = items.Distinct(new Class_reglementComparer());

Если вы хотите общее решение без бокса:

public class KeyBasedEqualityComparer<T, TKey> : IEqualityComparer<T>
{
    private readonly Func<T, TKey> _keyGetter;

    public KeyBasedEqualityComparer(Func<T, TKey> keyGetter)
    {
        _keyGetter = keyGetter;
    }

    public bool Equals(T x, T y)
    {
        return EqualityComparer<TKey>.Default.Equals(_keyGetter(x), _keyGetter(y));
    }

    public int GetHashCode(T obj)
    {
        TKey key = _keyGetter(obj);

        return key == null ? 0 : key.GetHashCode();
    }
}

public static class KeyBasedEqualityComparer<T>
{
    public static KeyBasedEqualityComparer<T, TKey> Create<TKey>(Func<T, TKey> keyGetter)
    {
        return new KeyBasedEqualityComparer<T, TKey>(keyGetter);
    }
}

использование:

KeyBasedEqualityComparer<Class_reglement>.Create(x => x.Numf)

IEquatable<T> может быть гораздо проще сделать это с помощью современных фреймворков.

вы получаете хороший простой bool Equals(T other) функция и нет никакого возиться с литьем или созданием отдельного класса.

public class Person : IEquatable<Person>
{
    public Person(string name, string hometown)
    {
        this.Name = name;
        this.Hometown = hometown;
    }

    public string Name { get; set; }
    public string Hometown { get; set; }

    // can't get much simpler than this!
    public bool Equals(Person other)
    {
        return this.Name == other.Name && this.Hometown == other.Hometown;
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode();  // see other links for hashcode guidance 
    }
}

обратите внимание, что вы должны реализовать GetHashCode при использовании этого в словаре или с чем-то вроде Distinct.

PS. Я не думаю, что какие-либо пользовательские методы Equals работают с Entity framework непосредственно на стороне базы данных (я думаю, вы это знаете, потому что вы это делаете AsEnumerable), но это гораздо более простой способ сделать простые равенства для общего случая.

если что-то не работает (например, повторяющиеся ключевые ошибки при выполнении ToDictionary) поместите точку останова внутри Equals, чтобы убедиться, что она поражена, и убедитесь, что у вас есть GetHashCode определено (с ключевым словом override).