Ayende @ Rahien

Hi!
My name is Oren Eini
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:

ayende@ayende.com

+972 52-548-6969

, @ Q c

Posts: 5,971 | Comments: 44,508

filter by tags archive

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

Mark Monster

What do you mean by method instantiation? Instantiation using delegates for named and anonymous method?

Ayende Rahien

I mean by the method object itself, no delegates involved.

This is probably mentioned in the docs, but I have no idea where

josh

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?

Thomas Krause

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?

Ayende Rahien

Yes, because if it has the same declaring type, and the same name & parameters, you can define another method with a different name

Ayende Rahien

Thomas ,

Great catch, I'll add that

Oran

"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

Comments have been closed on this topic.

FUTURE POSTS

  1. Paying the rent online - about one day from now

There are posts all the way to Aug 03, 2015

RECENT SERIES

  1. Production postmortem (5):
    29 Jul 2015 - The evil licensing code
  2. Career planning (6):
    24 Jul 2015 - The immortal choices aren't
  3. API Design (7):
    20 Jul 2015 - We’ll let the users sort it out
  4. What is new in RavenDB 3.5 (3):
    15 Jul 2015 - Exploring data in the dark
  5. The RavenDB Comic Strip (3):
    28 May 2015 - Part III – High availability & sleeping soundly
View all series

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats