Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do 2 delegate instances return the same hashcode?

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 
like image 751
leppie Avatar asked Jul 08 '11 12:07

leppie


People also ask

Why are the hash codes different?

Why? An object's hashCode method must take the same fields into account as its equals method. By overriding the equals method, you're declaring some objects as equal to other objects, but the original hashCode method treats all objects as different. So you will have equal objects with different hash codes.

What does hashCode combine do?

Combines two values into a hash code.


2 Answers

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

like image 61
Aliostad Avatar answered Oct 03 '22 20:10

Aliostad


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.

like image 21
CodesInChaos Avatar answered Oct 03 '22 21:10

CodesInChaos