为什么2个委托实例返回相同的哈希码?

时间:2021-03-05 16:48:10

Take the following:

请采取以下措施:

  var x =  new Action(() => { Console.Write("") ; });
  var y = new Action(() => { });
  var a = x.GetHashCode();
  var b = y.GetHashCode();
  Console.WriteLine(a == b);
  Console.WriteLine(x == y);

This will print:

这将打印:

True
False

Why is the hashcode the same?

为什么哈希码相同?

It is kinda surprising, and will make using delegates in a Dictionary as slow as a List (aka O(n) for lookups).

这有点令人惊讶,并且会使字典中的委托使用像列表一样慢(也就是查找的O(n))。

Update:

更新:

The question is why. IOW who made such a (silly) decision?

问题是为什么。 IOW谁做出这样一个(愚蠢的)决定?

A better hashcode implementation would have been:

一个更好的哈希码实现应该是:

return Method ^ Target == null ? 0 : Target.GetHashcode();
// where Method is IntPtr

4 个解决方案

#1


9  

Easy! Since here is the implementation of the GetHashCode (sitting on the base class Delegate):

简单!由于这里是GetHashCode的实现(坐在基类Delegate上):

public override int GetHashCode()
{
    return base.GetType().GetHashCode();
}

(sitting on the base class MulticastDelegate which will call above):

(坐在将要调用上面的基类MulticastDelegate):

public sealed override int GetHashCode()
{
    if (this.IsUnmanagedFunctionPtr())
    {
        return ValueType.GetHashCodeOfPtr(base._methodPtr);
    }
    object[] objArray = this._invocationList as object[];
    if (objArray == null)
    {
        return base.GetHashCode();
    }
    int num = 0;
    for (int i = 0; i < ((int) this._invocationCount); i++)
    {
        num = (num * 0x21) + objArray[i].GetHashCode();
    }
    return num;
}

Using tools such as Reflector, we can see the code and it seems like the default implementation is as strange as we see above.

使用Reflector之类的工具,我们可以看到代码,看起来默认实现和我们上面看到的一样奇怪。

The type value here will be Action. Hence the result above is correct.

这里的类型值将是Action。因此上面的结果是正确的。

UPDATE

#2


2  

My first attempt of a better implementation:

我第一次尝试更好地实施:

public class DelegateEqualityComparer:IEqualityComparer<Delegate>
{
    public bool Equals(Delegate del1,Delegate del2)
    {
        return (del1 != null) && del1.Equals(del2);
    }

    public int GetHashCode(Delegate obj)
    {
            if(obj==null)
                return 0;
            int result = obj.Method.GetHashCode() ^ obj.GetType().GetHashCode();
            if(obj.Target != null)
                result ^= RuntimeHelpers.GetHashCode(obj);
            return result;
    }
}

The quality of this should be good for single cast delegates, but not so much for multicast delegates (If I recall correctly Target/Method return the values of the last element delegate).

这对于单播代表来说应该是好的,但对于多播代表来说并不是那么多(如果我没记错,Target / Method会返回最后一个元素委托的值)。

But I'm not really sure if it fulfills the contract in all corner cases.

但我不确定它是否符合所有角落案件的合同。

Hmm it looks like quality requires referential equality of the targets.

嗯,看起来质量需要目标的参考平等。

#3


1  

This smells like some of the cases mentioned in this thread, maybe it will give you some pointers on this behaviour. else, you could log it there :-)

这闻起来像这个帖子中提到的一些情况,也许它会给你一些关于这种行为的指示。否则,你可以在那里登录:-)

What's the strangest corner case you've seen in C# or .NET?

你在C#或.NET中看到的最奇怪的角落是什么?

Rgds GJ

Rgds GJ

#4


1  

From MSDN :

来自MSDN:

The default implementation of GetHashCode does not guarantee uniqueness or consistency; therefore, it must not be used as a unique object identifier for hashing purposes. Derived classes must override GetHashCode with an implementation that returns a unique hash code. For best results, the hash code must be based on the value of an instance field or property, instead of a static field or property.

GetHashCode的默认实现不保证唯一性或一致性;因此,它不能用作散列目的的唯一对象标识符。派生类必须使用返回唯一哈希码的实现来覆盖GetHashCode。为获得最佳结果,哈希代码必须基于实例字段或属性的值,而不是静态字段或属性。

So if you have not overwritten the GetHashCode method, it may return the same. I suspect this is because it generates it from the definition, not the instance.

因此,如果您没有覆盖GetHashCode方法,它可能会返回相同的内容。我怀疑这是因为它从定义而不是实例生成它。

#1


9  

Easy! Since here is the implementation of the GetHashCode (sitting on the base class Delegate):

简单!由于这里是GetHashCode的实现(坐在基类Delegate上):

public override int GetHashCode()
{
    return base.GetType().GetHashCode();
}

(sitting on the base class MulticastDelegate which will call above):

(坐在将要调用上面的基类MulticastDelegate):

public sealed override int GetHashCode()
{
    if (this.IsUnmanagedFunctionPtr())
    {
        return ValueType.GetHashCodeOfPtr(base._methodPtr);
    }
    object[] objArray = this._invocationList as object[];
    if (objArray == null)
    {
        return base.GetHashCode();
    }
    int num = 0;
    for (int i = 0; i < ((int) this._invocationCount); i++)
    {
        num = (num * 0x21) + objArray[i].GetHashCode();
    }
    return num;
}

Using tools such as Reflector, we can see the code and it seems like the default implementation is as strange as we see above.

使用Reflector之类的工具,我们可以看到代码,看起来默认实现和我们上面看到的一样奇怪。

The type value here will be Action. Hence the result above is correct.

这里的类型值将是Action。因此上面的结果是正确的。

UPDATE

#2


2  

My first attempt of a better implementation:

我第一次尝试更好地实施:

public class DelegateEqualityComparer:IEqualityComparer<Delegate>
{
    public bool Equals(Delegate del1,Delegate del2)
    {
        return (del1 != null) && del1.Equals(del2);
    }

    public int GetHashCode(Delegate obj)
    {
            if(obj==null)
                return 0;
            int result = obj.Method.GetHashCode() ^ obj.GetType().GetHashCode();
            if(obj.Target != null)
                result ^= RuntimeHelpers.GetHashCode(obj);
            return result;
    }
}

The quality of this should be good for single cast delegates, but not so much for multicast delegates (If I recall correctly Target/Method return the values of the last element delegate).

这对于单播代表来说应该是好的,但对于多播代表来说并不是那么多(如果我没记错,Target / Method会返回最后一个元素委托的值)。

But I'm not really sure if it fulfills the contract in all corner cases.

但我不确定它是否符合所有角落案件的合同。

Hmm it looks like quality requires referential equality of the targets.

嗯,看起来质量需要目标的参考平等。

#3


1  

This smells like some of the cases mentioned in this thread, maybe it will give you some pointers on this behaviour. else, you could log it there :-)

这闻起来像这个帖子中提到的一些情况,也许它会给你一些关于这种行为的指示。否则,你可以在那里登录:-)

What's the strangest corner case you've seen in C# or .NET?

你在C#或.NET中看到的最奇怪的角落是什么?

Rgds GJ

Rgds GJ

#4


1  

From MSDN :

来自MSDN:

The default implementation of GetHashCode does not guarantee uniqueness or consistency; therefore, it must not be used as a unique object identifier for hashing purposes. Derived classes must override GetHashCode with an implementation that returns a unique hash code. For best results, the hash code must be based on the value of an instance field or property, instead of a static field or property.

GetHashCode的默认实现不保证唯一性或一致性;因此,它不能用作散列目的的唯一对象标识符。派生类必须使用返回唯一哈希码的实现来覆盖GetHashCode。为获得最佳结果,哈希代码必须基于实例字段或属性的值,而不是静态字段或属性。

So if you have not overwritten the GetHashCode method, it may return the same. I suspect this is because it generates it from the definition, not the instance.

因此,如果您没有覆盖GetHashCode方法,它可能会返回相同的内容。我怀疑这是因为它从定义而不是实例生成它。