Как использовать 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 ответов:
неудивительно, учитывая ваш
GetHashCode
реализация, которая всегда возвращает одно и то же значение.Distinct
полагается на хорошую хэш-функцию для эффективной работы.при реализации интерфейсов классов вы должны прочитать их документация первый, в противном случае вы не знаете, какой контракт вы должны реализовать.1
в вашем коде решение состоит в том, чтобы переслать
GetHashCode
toClass_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).