Method Equality
The CLR team deserve a truly great appreciation for making generics works at all. When you get down to it, it is amazingly complex. Most of the Rhino Mocks bugs stems from having to work at that level. Here is one example, comparing method equality. Let us take this simple example:
[TestFixture] public class WeirdStuff { public class Test<T> { public void Compare() { Assert.AreEqual(GetType().GetMethod("Compare"), MethodInfo.GetCurrentMethod() ); } } [Test] public void ThisIsWeird() { new Test<int>().Compare(); } }
This is one of those things that can really bites you. And it fails only if the type is a generic type, even though the comparison is made of the closed generic version of the type. Finding the root cause was fairly hard, and naturally the whole thing is internal, but eventually I managed to come up with a way to compare them safely:
private static bool AreMethodEquals(MethodInfo left, MethodInfo right) { if (left.Equals(right)) return true; // GetHashCode calls to RuntimeMethodHandle.StripMethodInstantiation() // which is needed to fix issues with method equality from generic types. if (left.GetHashCode() != right.GetHashCode()) return false; if (left.DeclaringType != right.DeclaringType) return false; ParameterInfo[] leftParams = left.GetParameters(); ParameterInfo[] rightParams = right.GetParameters(); if (leftParams.Length != rightParams.Length) return false; for (int i = 0; i < leftParams.Length; i++) { if (leftParams[i].ParameterType != rightParams[i].ParameterType) return false; } if (left.ReturnType != right.ReturnType) return false; return true; }
The secret here is with the call to GetHashCode, which remove the method instantiation code, which is fairly strange concept, because I wasn't aware that you can instantiate methods :-)
Comments
What do you mean by method instantiation? Instantiation using delegates for named and anonymous method?
I mean by the method object itself, no delegates involved.
This is probably mentioned in the docs, but I have no idea where
Nice solution. What about method access privileges on each? What if one method is public and the other is internal or protected? Should they still be considered equal to each other?
Shouldn't you also check the method name for equality?
Two methods could be declared on the same class with the same signature (parameters and return type) but different names. The Hash code will of course be different in most of the cases, but this isn't guaranteed... Am I missing something here?
Yes, because if it has the same declaring type, and the same name & parameters, you can define another method with a different name
Thomas ,
Great catch, I'll add that
"the comparison is made of the closed generic version of the type" - actually, it's not. GetType().GetMethod("Compare").DeclaringType is closed. MethodInfo.GetCurrentMethod().DeclaringType is open. See the Community Content section for MethodBase.GetCurrentMethod for details:
http://msdn2.microsoft.com/en-us/library/system.reflection.methodbase.getcurrentmethod.aspx
Basically, MethodBase.GetCurrentMethod doesn't behave as one would expect.
Also notice that ContainsGenericParameters is false in the first MethodInfo while it is true in the second. According to the following link, this means the second one is an open constructed generic method:
http://blogs.msdn.com/parthopdas/archive/2005/10/21/483463.aspx
Comment preview