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

时间:2011-07-08 12:03:15

标签: c# .net delegates hashcode

采取以下措施:

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

这将打印:

True
False

为什么哈希码相同?

这有点令人惊讶,并且会使Dictionary中的代表与List一样慢(对O(n)进行查找)。

更新

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

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

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

4 个答案:

答案 0 :(得分:9)

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

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

(坐在将在上面调用的基础班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;
}

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

此处的类型值为Action。因此,上面的结果是正确

更新

答案 1 :(得分:3)

我首次尝试更好地实施:

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

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

但我不确定它是否在所有极端情况下都符合合同。

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

答案 2 :(得分:1)

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

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

Rgds GJ

答案 3 :(得分:1)

来自MSDN:

  

默认实现   GetHashCode不保证   独特性或一致性;因此,   它不能用作唯一对象   用于散列目的的标识符。   派生类必须覆盖   带有实现的GetHashCode   返回唯一的哈希码。对于   最好的结果,哈希码必须是   基于实例的值   字段或属性,而不是静态的   田地或财产。

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