4

非ジェネリッククラスのオープンジェネリックメソッドを識別するMethodInfoインスタンスがある場合、次の擬似コードを検討してください。

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");

このコードを本文に含むメソッド「voidGeneratedMethod<T>()」を生成します。

IL.Emit(OpCodes.Ldtoken, methodInfo);
IL.Emit(OpCodes.Call, methodInfoPrintMethodInfo);

GeneratedMethod <int>()を呼び出すと、.Net3.5での出力は次のようになります。

Method: System.Object Method[Int32]()

.Net 4.0を使用している間は、次のようになります。

Method: System.Object Method[T]()

したがって、.Net 2.0 / 3.5では、ldtokenに対して生成されたILには、GeneratedMethod<T>が呼び出されたときに指定されたtype引数でインスタンス化された汎用FooMethod<>を識別するメタデータトークンが含まれるようです。

ただし、.Net 4.0では、ldtokenにはオープンジェネリック型を識別するメタデータが含まれます。

.Net 3.5の場合に起こっていることをサポートするドキュメントを見つけるのに問題があります(実際、生成されたメソッド自体が汎用でない場合は完全に失敗するはずです)-.Net4の動作はより論理的です。変更のドキュメントも見つかりません。これは、現在修正されている以前のバージョンのバグですか?

4

1 に答える 1

5

ldtoken生成されたコードを逆アセンブルすると、.NET 3.5 では、オープン ジェネリック メソッドに関連する命令が次のように出力されることがわかります。

.method public static void  GeneratedMethod<T>() cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  IL_0000:  ldtoken    method instance void [ConsoleApplication16]ConsoleApplication16.Program/Foo::FooMethod<!!0>()
  IL_0005:  call       void [ConsoleApplication16]ConsoleApplication16.Program::PrintMethodInfo(valuetype [mscorlib]System.RuntimeMethodHandle)
  IL_000a:  ret
} // end of method TestType::GeneratedMethod

構文!!0は周囲のメソッド ( GeneratedMethod) の型パラメーターへの参照であるため、メソッドは に属するFooインスタンス化されて読み込まれます。(実際、これは に対して発行されるものと同じ IL です。) この参照は、がまったくジェネリックではない場合でも発行されます。結果として得られるプログラムは検証できなくなります (実行すると、BadImageFormatException が発生します)。TGeneratedMethod<T>IL.Emit (OpCodes.Ldtoken, methodInfo.MakeGenericMethod (<typeParameterOfGeneratedMethod>))!!0GeneratedMethod

これは明らかにバグであり、.NET 4 では、(逆アセンブルされた) 発行されたコードが次のようになるため、これは修正されているようです。

.method public static void  GeneratedMethod<T>() cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  IL_0000:  ldtoken    method instance void [ConsoleApplication16]ConsoleApplication16.Program/Foo::FooMethod<[1]>()
  IL_0005:  call       void [ConsoleApplication16]ConsoleApplication16.Program::PrintMethodInfo(valuetype [mscorlib]System.RuntimeMethodHandle)
  IL_000a:  ret
} // end of method TestType::GeneratedMethod

ご覧のとおり、署名はインスタンス化されていないものを参照するようになりましたFooMethod(IL アセンブリでは、これは として示されますFooMethod[1])。

そうです、これは .NET 4 で修正された .NET 3.5 のバグのように見えldtokenます。Reflection.Emit がジェネリック メソッドを開くための参照を正しく発行しなかっただけです。これは、以前のIL アセンブラーにはオープン ジェネリック メソッドを示す構文さえなかったという事実にも関連していると思われます。

于 2012-10-15T09:43:15.980 に答える