Given a MethodInfo instance that identifies an open generic method of a non-generic class, consider the following pseudocode:
class Foo { void FooMethod<T>() {} }
public static void PrintMethodInfo(RuntimeMethodHandle methodHandle)
{
var mi = (MethodInfo) MethodBase.GetMethodFromHandle(methodHandle);
Console.WriteLine("Method: "+mi.ToString());
}
var methodInfo = typeof(Foo).GetMethod("FooMethod");
Generate a method “void GeneratedMethod<T>()” containing this code in the body:
IL.Emit(OpCodes.Ldtoken, methodInfo);
IL.Emit(OpCodes.Call, methodInfoPrintMethodInfo);
Calling GeneratedMethod<int>(), the output on .Net 3.5 will be:
Method: System.Object Method[Int32]()
While on .Net 4.0 it will be:
Method: System.Object Method[T]()
So it appears that in .Net 2.0/3.5, the generated IL for the ldtoken will contain a metadata token that identifies the generic FooMethod<> instantiated with the type argument given when GeneratedMethod<T> was called.
In .Net 4.0, however, the ldtoken will contain metadata that identifies the open generic type.
I’m having trouble finding documentation that support what’s happening in the .Net 3.5 case (and indeed, it should fail completely if the generated method isn’t itself generic) – the .Net 4 behaviour seems more logical. I can’t find any documentation of the change either. Is this a bug in the earlier version that has now been fixed?
When you disassemble the generated code, you can see that in .NET 3.5, the
ldtokeninstruction in connection to the open generic method is emitted as follows:The syntax
!!0is a reference to the type parameter of the surrounding method (GeneratedMethod), so theFoomethod is loaded instantiated with theTbelonging toGeneratedMethod<T>. (In fact, this is the same IL as would be emitted forIL.Emit (OpCodes.Ldtoken, methodInfo.MakeGenericMethod (<typeParameterOfGeneratedMethod>)).) This!!0reference is emitted even whenGeneratedMethodis not generic at all – the resulting program is then no longer verifiable (and, when executed, results in a BadImageFormatException).This is clearly a bug, and in .NET 4, this seems to be fixed, as the (disassembled) emitted code now looks like this:
As you can see, the signature now refers to the uninstantiated
FooMethod(in IL assembly, this is denoted asFooMethod[1]).So yes, this looks like a bug in .NET 3.5 that was fixed with .NET 4. However, it doesn’t seem to be a change in the semantics of
ldtoken; it was just that Reflection.Emit didn’t emit references to open generic methods correctly. I suspect it’s also connected to the fact that IL assembler didn’t even have a syntax to denote open generic methods in the past.