Как использовать 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;
}
}
Ответы
Ответ 1
Неудивительно, учитывая вашу реализацию GetHashCode
, которая всегда возвращает одно и то же значение. Distinct
полагается на хорошую хеш-функцию для эффективной работы.
При реализации интерфейсов классов вам нужно сначала прочитать документацию, иначе вы не знаете, какой контракт вы предполагаете для реализации. 1
В вашем коде решение должно пересылать GetHashCode
в Class_reglement.Numf.GetHashCode
и соответствующим образом реализовывать его.
Кроме того, ваш метод Equals
заполнен ненужным кодом. Его можно было бы переписать следующим образом (та же семантика, 1/4 кода, более читаемая):
public bool Equals(Class_reglement x, Class_reglement y)
{
return x.Numf == y.Numf;
}
Кроме того, вызов ToList
не нужен и занимает много времени: AddRange
принимает любое IEnumerable
, поэтому преобразование в List
не требуется. AsEnumerable
также избыточен здесь, так как обработка результата в AddRange
все равно приведет к этому.
1 Реализация кода, не зная, что он на самом деле делает, называется культовое программирование груза. Это удивительно распространенная практика. Это принципиально не работает.
Ответ 2
Попробуйте этот код:
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();
Ответ 3
Включение вашего класса сравнения (или, более конкретно, вызов AsEnumerable
, который вам нужно использовать для его работы) означает, что логика сортировки переходила от базы данных к клиенту базы данных (ваше приложение). Это означало, что вашему клиенту теперь нужно получить и обработать большее количество записей, что всегда будет менее эффективным, если вы выполняете поиск в базе данных, где могут использоваться соответствующие индексы.
Вы должны попытаться разработать предложение where, которое удовлетворяет вашим требованиям вместо этого, см. Использование IEqualityComparer с LINQ to Entities За исключением предложения для более подробной информации.
Ответ 4
Просто код с реализацией GetHashCode
и NULL
проверки:
public class Class_reglementComparer : IEqualityComparer<Class_reglement>
{
public bool Equals(Class_reglement x, Class_reglement y)
{
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.Numf == y.Numf;
}
public int GetHashCode(Class_reglement product)
{
//Check whether the object is null
if (Object.ReferenceEquals(product, 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());