I’m encountering quite a strange behaviour when comparing MethodBase instances with Equals method using .NET Framework 3.5 – it just fails with NullReferenceException in case when comparing compiler-defined constructor method object with open generic method object.
Here is the repro code:
class TheClass
{
public T TheMethod<T>()
{
return default(T);
}
}
class Program
{
private static void Main(string[] args)
{
var ctor = typeof(TheClass).GetConstructors().Single();
var generic = typeof(TheClass).GetMethods().Single(x => x.Name == "TheMethod");
Console.WriteLine(generic.Name); // TheMethod
Console.WriteLine(generic.GetType().Name); // RuntimeMethodInfo
Console.WriteLine(ctor.Name); // .ctor
Console.WriteLine(ctor.GetType().Name); // RuntimeConstructorInfo
Console.WriteLine(generic.Equals(ctor)); // throws NullReferenceException
Console.ReadKey();
}
}
It works fine in .NET 4.0.
I’ve looked into RuntimeMethodInfo.Equals implementations in 3.5 and 4.0 using decompiler, here is the interesting part:
.NET 3.5
if (!this.IsGenericMethod)
return obj == this;
RuntimeMethodInfo runtimeMethodInfo = obj as RuntimeMethodInfo;
if (this.GetMethodHandle().StripMethodInstantiation() != runtimeMethodInfo.GetMethodHandle().StripMethodInstantiation() || runtimeMethodInfo == null || !runtimeMethodInfo.IsGenericMethod)
return false;
// ...
.NET 4.0
if (!this.IsGenericMethod)
return obj == this;
RuntimeMethodInfo runtimeMethodInfo = obj as RuntimeMethodInfo;
if ((MethodInfo) runtimeMethodInfo == (MethodInfo) null || !runtimeMethodInfo.IsGenericMethod || RuntimeMethodHandle.StripMethodInstantiation((IRuntimeMethodInfo) this).Value.Value != RuntimeMethodHandle.StripMethodInstantiation((IRuntimeMethodInfo) runtimeMethodInfo).Value.Value)
return false;
In .NET 4.0 the null-check was moved before using possibly-null runtimeMethodInfo variable. For me, 3.5 behaviour seems to be a framework bug, isn’t it?
So the question is – is there a workaround or a way to compare these objects safely? Note that in real code I’m not calling Equals directly, but using it somewhere implicitly in collections etc., so catching NullReferenceExceptions doesn’t sound well.
Doesn’t look like there’s a good workaround, other than writing a replacement
Equalsmethod. Or at least something that will check thectorvalue before you call the runtime’sEqualsmethod.For the collection, create an
EqualityComparerto do custom comparisons. That’ll eliminate the call to the defaultEqualsmethod.