如何使用IEqualityComparer


97

我的数据库中有一些铃铛号相同。我希望所有这些都没有重复。我创建了一个compare类来完成这项工作,但是该函数的执行会导致该函数的延迟很大,没有区别,从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;
    }
}


该博客介绍了如何完美使用IEqualityComparer:blog.alex-turok.com/2013/03/c-linq-and-iequalitycomparer.html
Jeremy Ray Brown,

Answers:


174

您的GetHashCode实现始终返回相同的值。Distinct依靠良好的哈希函数可以有效地工作,因为它在内部构建哈希表

在实现类的接口时,重要的一点是阅读文档,以了解您应该实现哪个合同。1个

在你的代码,解决办法是转发GetHashCodeClass_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在不知道代码实际作用的情况下编写代码称为“ 货物崇拜”编程。这是一种出奇的普遍做法。从根本上来说,它是行不通的。


20
x或y为空时,您的Equals失败。
dzendras 2012年

4
@dzendras与相同GetHashCode。但是,请注意,的文档IEqualityComparer<T>未指定如何处理null参数-但本文中提供的示例均无法处理null
Konrad Rudolph

50
哇。 憎恶是不必要的苛刻。我们在这里是互相帮助,而不是侮辱。我想这会让有些人笑,但是我建议您删除它。
杰西

5
+1,让我在Wiki上阅读有关“货物崇拜编程”的信息,然后将我的Skype标记行更改为“ //深魔法从这里开始...随后是沉重的巫术”。
亚历克斯

4
@NeilBenn您将坦率的建议误认为是无礼。由于OP接受了答案(而且,我可能会注意到,版本更严格!),所以他们似乎并没有犯同样的错误。我不确定您为什么认为提供建议是不礼貌的,但是当您说“那个人不需要演讲”时,您会弄错。我强烈不同意:这必要的讲座,它引起了我的注意。所写的代码是错误的,并且基于不良的工作实践。不指出这一点将是不利的,并且完全没有帮助,因为那样的话,OP无法改善它们的工作方式。
康拉德·鲁道夫

47

试试下面的代码:

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(); 

19
GetHashCode还需要使用该表达式:return _expr.Invoke(obj).GetHashCode();有关用法,请参阅此帖子
orad 2014年

3

只需代码,并实现GetHashCodeNULL验证:

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

示例:Numf区分的 Class_reglement列表

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

2

包含比较类(或更具体地说,AsEnumerable需要使用比较类才能使其工作)意味着排序逻辑已从基于数据库服务器变为在数据库客户端(您的应用程序)上。这意味着您的客户端现在需要检索然后处理大量的记录,这总是比在可以使用适当索引的数据库上执行查询效率低。

您应该尝试开发一个满足您要求的where子句,有关更多详细信息,请参见将IEqualityComparer与LINQ to Entities Except子句一起使用。


2

如果您想不装箱的通用解决方案:

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)

0

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方法可以直接在数据库端与实体框架一起使用(我想您知道这是因为您执行AsEnumerable),但这是在一般情况下执行简单Equals的简单得多的方法。

如果事情似乎不起作用(例如执行ToDictionary时出现重复的键错误),则在Equals内放置一个断点,以确保它被击中并确保已GetHashCode定义(使用override关键字)。


1
您仍然需要检查是否为空
disklosr

我从未遇到过这种情况,但下次会记得。您在List <T>中是否有null或类似的内容?
Simon_Weaver

1
.Equals()您看来已与other.Hometown自身进行比较的方法下,而不是this.Hometown
Jake Stokes,

哎呀。固定错字:)
Simon_Weaver
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.