Dictionary<,> のクローンを作成する動的メソッドを作成しようとしています。
以下に示すコードは、例外をスローします。
未処理の例外: System.Reflection.TargetInvocationException: 呼び出しのターゲットによって例外がスローされました。 ---> System.InvalidProgramException: 共通言語ランタイムが無効なプログラムを検出しました。 Clone(Dictionary`2)で --- 内部例外スタック トレースの終了 --- System.RuntimeMethodHandle.InvokeMethod (オブジェクト ターゲット、オブジェクト [] 引数、署名 sig、ブール コンストラクター) で System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal (オブジェクト obj、オブジェクト [] パラメーター、オブジェクト [] 引数) で System.Delegate.DynamicInvokeImpl (オブジェクト [] 引数) で x:\MsilTests\MsilTests\DynamicHelper.cs:line 17 の MsilTests.DynamicHelper.Clone[TKey,TValue](Dictionary`2 source) で x:\MsilTests\MsilTests\Program.cs:line 11 の MsilTests.Program.Main(String[] args) で
この例外は、次の行を追加したときに発生します:
generator.Emit(OpCodes.Callvirt, enumeratorType.GetMethod("MoveNext"));
この例外がスローされる理由がわかりません。誰かがこの問題を解決するのを手伝ってくれるなら、私はとても感謝しています.
public static class DynamicHelper
{
public static Dictionary<TKey, TValue> Clone<TKey, TValue>(Dictionary<TKey, TValue> source)
{
var type = typeof (Dictionary<,>).MakeGenericType(typeof (TKey), typeof (TValue));
var genericFunc = typeof(Func<,>);
genericFunc = genericFunc.MakeGenericType(type, type);
var method = new DynamicMethod("Clone", type, new[] { type }, Assembly.GetExecutingAssembly().ManifestModule, true);
GenerateMsil(method, type);
return (Dictionary<TKey, TValue>)method.CreateDelegate(genericFunc).DynamicInvoke(source);
}
private static void GenerateMsil(DynamicMethod method, Type type)
{
var genArgs = type.GetGenericArguments();
var keyType = genArgs[0];
var valueType = genArgs[0];
var pairType = typeof(KeyValuePair<,>).MakeGenericType(keyType, valueType);
var enumeratorType = typeof(Dictionary<,>.Enumerator).MakeGenericType(keyType, valueType);
var enumerableType = typeof (IEnumerable<>).MakeGenericType(pairType);
var generator = method.GetILGenerator();
var labelRet = generator.DefineLabel();
var labelEndFinally = generator.DefineLabel();
var labelMove = generator.DefineLabel();
var labelWhile = generator.DefineLabel();
var source = generator.DeclareLocal(type); //local_0
var target = generator.DeclareLocal(type); //local_1
var enumerator = generator.DeclareLocal(enumeratorType); //local_2
var pair = generator.DeclareLocal(pairType); //local_3
var key = generator.DeclareLocal(keyType); //local_4
var value = generator.DeclareLocal(valueType); //local_5
/*Stack */
/*[0] */
/*[1] +1 */generator.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
/*[0] -1 */generator.Emit(OpCodes.Stloc, target);
/*[1] +1 */generator.Emit(OpCodes.Ldarg_0);
/*[0] -1 */generator.Emit(OpCodes.Stloc, source);
/*[1] +1 */generator.Emit(OpCodes.Ldloc, source);
/*[1] -1+1*/generator.Emit(OpCodes.Callvirt, type.GetMethod("GetEnumerator"));
/*[0] -1 */generator.Emit(OpCodes.Stloc, enumerator);
var tryFinally = generator.BeginExceptionBlock();
// try {
generator.Emit(OpCodes.Br_S, labelMove);
generator.MarkLabel(labelWhile);
/*[1] +1 */generator.Emit(OpCodes.Ldloca, enumerator);
/*[1] -1+1*/generator.Emit(OpCodes.Callvirt, enumeratorType.GetProperty("Current").GetGetMethod());
/*[0] -1 */generator.Emit(OpCodes.Stloc, pair);
/*[1] +1 */generator.Emit(OpCodes.Ldloca, pair);
/*[1] -1+1*/generator.Emit(OpCodes.Callvirt, pairType.GetProperty("Key").GetGetMethod());
/*[0] -1 */generator.Emit(OpCodes.Stloc, key);
/*[1] +1 */generator.Emit(OpCodes.Ldloca, pair);
/*[1] -1+1*/generator.Emit(OpCodes.Callvirt, pairType.GetProperty("Value").GetGetMethod());
/*[0] -1 */generator.Emit(OpCodes.Stloc, value);
/*[2] +1 */generator.Emit(OpCodes.Ldloc, target);
/*[1] +1 */generator.Emit(OpCodes.Ldloc, key);
/*[3] +1 */generator.Emit(OpCodes.Ldloc, value);
/*[0] -3 */generator.Emit(OpCodes.Callvirt, type.GetMethod("Add"));
generator.MarkLabel(labelMove);
/*[1] +1 */generator.Emit(OpCodes.Ldloc, enumerator);
/*[1] -1+1*/generator.Emit(OpCodes.Callvirt, enumeratorType.GetMethod("MoveNext"));
/*[0] -1 */generator.Emit(OpCodes.Brtrue_S, labelWhile);
generator.Emit(OpCodes.Leave_S, labelRet);
// } finally {
generator.BeginFinallyBlock();
/*[1] +1 */generator.Emit(OpCodes.Ldloca, enumerator);
/*[0] -1 */generator.Emit(OpCodes.Brfalse_S, labelEndFinally);
/*[1] +1 */generator.Emit(OpCodes.Ldloc, enumerator);
/*[0] -1 */generator.Emit(OpCodes.Callvirt, enumeratorType.GetMethod("Dispose"));
generator.MarkLabel(labelEndFinally);
generator.Emit(OpCodes.Endfinally);
// }
generator.EndExceptionBlock();
generator.MarkLabel(labelRet);
/*[1] +1 */generator.Emit(OpCodes.Ldloc, target);
/*[0] -1 */generator.Emit(OpCodes.Ret);
}
}
更新:私の問題への助けと注意を払ってくれてありがとう。私はすでに問題を解決しました.Dictionary<,>.EnumeratorでLdlocを使用することですが、Dictionary<,>.Enumeratorは値型であり、Ldlocaを使用してローカル変数のアドレスをCallまたはCallvirtに渡すことが重要です. ソースを正しいコードで更新しました。