C# 使用Equals。GetHashCode不是

C# 使用Equals。GetHashCode不是,c#,gethashcode,C#,Gethashcode,我已经实现了以下类: public class carComparer : IEqualityComparer<Car> { public bool Equals(Car car1, Car car2) { if (car1 == null || car2 == null) return false; return (car1.descri

我已经实现了以下类:

public class carComparer : IEqualityComparer<Car>
    {
        public bool Equals(Car car1, Car car2)
        {
                if (car1 == null || car2 == null)
                    return false;

                return (car1.description == car2.description);
        }

        public int GetHashCode(Car car)
        {
            unchecked 
            {
                int hash = 17;
                hash = hash * 29 + car.id.GetHashCode();
                hash = hash * 29 + car.description.GetHashCode();
                return hash;
            }
        }

    }
公共类carComparer:IEqualityComparer
{
公共布尔等于(Car car1,Car car2)
{
if(car1==null | | car2==null)
返回false;
返回(car1.description==car2.description);
}
公共int GetHashCode(汽车)
{
未经检查
{
int hash=17;
hash=hash*29+car.id.GetHashCode();
hash=hash*29+car.description.GetHashCode();
返回散列;
}
}
}
现在看看这个:

Car p1 = new Car() { id = Guid.NewGuid(), description = "Test1" };
        Car p2 = new Car() { id = Guid.NewGuid(), description = "Test1" };
        Car p3 = new Car() { id = Guid.NewGuid(), description = "Test1" };
        Car p4 = new Car() { id = Guid.NewGuid(), description = "Test1" };
        var hash = new HashSet<Car>();
        hash.Add(p1);
        hash.Add(p2);

        var hash2 = new HashSet<Car>();
        hash2.Add(p3);
        hash2.Add(p4);

        var carComparer = new CarComparer();
        Assert.That(hash, Is.EquivalentTo(hash2).Using(carComparer));
carp1=newcar(){id=Guid.NewGuid(),description=“Test1”};
carp2=newcar(){id=Guid.NewGuid(),description=“Test1”};
Car p3=new Car(){id=Guid.NewGuid(),description=“Test1”};
Car p4=新车(){id=Guid.NewGuid(),description=“Test1”};
var hash=new HashSet();
hash.Add(p1);
hash.Add(p2);
var hash2=新的HashSet();
hash2.Add(p3);
hash2.Add(p4);
var carComparer=新的carComparer();
Assert.That(hash,Is.EquivalentTo(hash2).Using(carComparer));

我将断点放在.equals和.hashcode中。使用Equals;但GetHashCode不是。为什么?

GetHashCode通常用于哈希表查找

GetHashCode
不必保证唯一,因此不是有效的
IsEqual
测试

对于要使用的GetHashCode,请使用HashSet的以下构造函数:

因此,要使用
GetHashCode
方法,您需要使用:

var hash = new HashSet<Car>(carComparer);
出于显而易见的原因,这使得
比较器
成为只读属性

总之,是这样

未使用
GetHashCode
,因为它通常用于创建哈希表查找,所以在开始添加项之前,需要将其提供给哈希集


由于明显的原因,使用了
IsEqual
;如果不是:请参阅@dasblinkenlight的答案

发生这种情况是因为在
IseEquivalent
中使用了算法来确定等价性:实现从您期望的集合中构造一个他们称之为“集合计数”的对象,然后尝试从中逐个删除实际集合的项:

public bool TryRemove(IEnumerable c) {
    foreach (object o in c)
        if (!TryRemove(o))
            return false;
    return true;
}

public bool TryRemove(object o) {
    for (int index = 0; index < list.Count; index++)
        if (ItemsEqual(list[index], o)) {
            list.RemoveAt(index);
            return true;
        }
    return false;
}
public bool TryRemove(IEnumerable c){
foreach(c中的对象o)
如果(!TryRemove(o))
返回false;
返回true;
}
公共bool TryRemove(对象o){
for(int index=0;index
您可以看到,NUnit使用了相对低效的O(n2)算法,而不是为O(n)效率构建哈希集。这对于较大的集合来说很重要,但由于单元测试中的典型集合只有几个项,所以不会有明显的差异


ItemsEqual
使用相等比较器中的
Equals
,但它不需要哈希代码功能()。

您正在使用NUnit
Is.equaletto比较两个
哈希集。它没有理由调用
GetHashCode
——它基本上是比较两个集合的成员是否相等。这就是为什么从不调用
GetHashCode
,而调用
Equals
来比较来自不同
HashSet
s的两个项目是否相等的原因。您的哈希集也可以是列表或任何其他可枚举的—在比较两个集合时,这不会改变任何内容

当您将项添加到
HashSet
时,您可能希望调用
GetHashCode
——但事实并非如此,因为此时您的
carComparer
还未知——您不会将其传递给
HashSet
构造函数。如果你愿意这样做:

var hash = new HashSet<Car>(new carComparer());
var hash=newhashset(new carComparer());

然后,当您向相应的
HashSet
添加新项时,将调用
GetHashCode

什么是
Car
(和/或
Car
,它们是否等效)?您希望在哪里调用
GetHashCode
?为什么?在什么时候使用了equals?将代码放入Linqpad不会为我调用任何一种方法,这是意料之中的,因为您没有告诉hashset使用比较器。如果我做
var hash=newhashset(newcarcomparer())
然后它调用
GetHashCode
两次,我假设为您添加的每个对象调用一次……为什么要使用它?你哪儿也不叫它。您正在调用GetHashCode()离子car.id和car.description,但不是在car上。@VDN,我在Assert中调用它。您似乎没有抓住问题的重点。他把东西放在一个
HashSet
中,这就是他希望调用
GetHashCode
的原因,但事实并非如此。您的答案似乎没有解决这一点,而是讨论了这两种方法的一般性…但是,
IEqualityComparer
在测试相等性时不会以任何方式绑定到哈希集。对,只有在HashSet构造函数中传递carComparer时,才会在Add方法中使用carComparer。@Chris:虽然您有一点关于缺少问题的关键部分,但我认为您的观点是无效的,因为HashSet没有绑定到comparer,而在添加(并因此创建)哈希表时,comparer是至关重要的。无论如何,我更新了更多细节的答案,希望你能重新考虑你的评论;-)@史蒂芬:我确实认为现在的答案好多了,是的。什么时候调用GetHashCode?它是在将项添加到集合时调用的还是在调用.equals时调用的。抱歉搞混了,谢谢。你认为我的哈希代码的质量如何?我使用这个问题的答案作为模板:+1进行澄清,这很有意义。看起来对我来说没问题。请注意,您可能会覆盖
var hash = new HashSet<Car>(new carComparer());