简体   繁体   English

等于使用。 GetHashCode不是

[英]Equals is used. GetHashCode is not

I have implemented the class below: 我已经实现了以下课程:

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

    }

Now see this: 现在看到这个:

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

I put breakpoints in .equals and .hashcode. 我把断点放在.equals和.hashcode中。 Equals is used; 等于使用; but GetHashCode is not. 但GetHashCode不是。 Why? 为什么?

You are comparing two HashSet using NUnit Is.EquivalentTo . 您正在使用NUnit Is.EquivalentTo比较两个HashSet There is no reason for it to call GetHashCode - it basically compares two collections for equality of its members. 它没有理由调用GetHashCode - 它基本上比较了两个集合的成员是否相等。 That's why GetHashCode is never called and Equals is called to compare two items from different HashSet s for equality. 这就是为什么从不调用GetHashCode并调用Equals来比较来自不同HashSet的两个项目的相等性。 Your hashsets could as well be lists or any other enumerable - that doesn't change anything when comparing two collections. 您的哈希集也可以是列表或任何其他可枚举的 - 在比较两个集合时不会改变任何内容。

You might expect GetHashCode to be called when you add item to HashSet - but it's not so, because at this point your carComparer is not yet known - you don't pass it to HashSet constructor. 您可能希望在向HashSet添加项目时调用GetHashCode - 但事实并非如此,因为此时您的carComparer尚未知晓 - 您不会将其传递给HashSet构造函数。 If you will do it like this: 如果你这样做:

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

Then GetHashCode would be called when you add new item to corresponding HashSet . 然后在将新项添加到相应的HashSet时将调用GetHashCode

GetHashCode is typically used in a hash-table-lookup. GetHashCode通常用于散列表查找。

The GetHashCode does not have to be guaranteed unique and therefor is not a valid IsEqual test. GetHashCode不必保证唯一,因此不是有效的IsEqual测试。

For the GetHashCode to be used, use this constructor of the HashSet: 对于要使用的GetHashCode,请使用HashSet的此构造函数:

https://msdn.microsoft.com/en-us/library/bb359100(v=vs.110).aspx https://msdn.microsoft.com/en-us/library/bb359100(v=vs.110).aspx

So in order to use the GetHashCode method, you'll need to use: 因此,为了使用GetHashCode方法,您需要使用:

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

Note that the hash will be verified when adding your object to the hashset. 请注意,将对象添加到哈希集时将验证哈希。

The Comparer is then used in the HashSet.Add method, within this call: 然后在此调用中的HashSet.Add方法中使用Comparer:

private int InternalGetHashCode(T item)
{
  if ((object) item == null)
    return 0;

  //this._comparer is set during the constructor call
  return this._comparer.GetHashCode(item) & int.MaxValue;
}

For obvious reasons this makes the Comparer a read only property. 由于显而易见的原因,这使得Comparer成为只读属性。

So, to sum up; 所以,总结一下;

The GetHashCode is not used because, since it is typically used in a hash-table-lookup-creation, you'll need to provide it to the HashSet before you start adding items. 不使用GetHashCode ,因为它通常用于散列表查找创建,因此在开始添加项之前,您需要将其提供给HashSet。

The IsEqual is used due to obvious reasons; 由于显而易见的原因使用IsEqual ; and if it's not: see @dasblinkenlight's answer. 如果不是:请参阅@dasblinkenlight的回答。

This happens because of the algorithm used in IsEquivalent to decide equivalency: the implementation constructs what they call a "collection tally" object from the collection that you expect, and then try removing items of the actual collection from it one-by-one: 发生这种情况是因为IsEquivalent使用的算法决定了等价性:实现从您期望的集合构造它们所谓的“集合计数”对象,然后逐个尝试从中删除实际集合的项目:

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

You can see that NUnit uses a relatively inefficient O(n 2 ) algorithm instead of constructing a hash set for O(n) efficiency. 您可以看到NUnit使用效率相对较低的O(n 2 )算法,而不是为O(n)效率构造哈希集。 This would matter for larger sets, but since a typical collection in a unit test has only a few items, there would be no noticeable difference. 这对于较大的集合很重要,但由于单元测试中的典型集合只有少数项目,因此没有明显的差异。

ItemsEqual uses Equals from the equality comparer, but it has no need for hash code functionality ( source code ). ItemsEqual使用Equals比较器的Equals ,但它不需要哈希代码功能( 源代码 )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM