Why do 2 delegate instances return the same hashcode?

前端 未结 4 477
眼角桃花
眼角桃花 2021-01-31 16:55

Take the following:

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


        
相关标签:
4条回答
  • 2021-01-31 16:55

    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

    0 讨论(0)
  • 2021-01-31 17:02

    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.

    0 讨论(0)
  • 2021-01-31 17:07

    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

    0 讨论(0)
  • 2021-01-31 17:07

    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.

    0 讨论(0)
提交回复
热议问题