Why do 2 delegate instances return the same hashcode?

一笑奈何 提交于 2019-12-20 08:57:28

问题


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

Update:

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

A better hashcode implementation would have been:

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

回答1:


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

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

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

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.

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

UPDATE




回答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).

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:


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?

Rgds GJ




回答4:


From 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.

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.



来源:https://stackoverflow.com/questions/6624151/why-do-2-delegate-instances-return-the-same-hashcode

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!