2016-06-15 9 views
0

TypeBuilderで自分のタイプを構築していますが、別のオブジェクトから集めたmethodInfoを呼び出すこのメソッドに追加しようとしています。他のメソッドをIlGeneratorで呼び出す

問題は、ILGenerator.EmitまたはILGenerator.EmitCallの使用方法がわかりません。

il.EmitCall(OpCodes.Call, methodInfo, arguments)il.Emit(OpCodes.Call, methodInfo)を使用しようとしましたが、どちらも機能しませんでした。常に私は、このエラーました:

[InvalidProgramException: Common Language Runtime detected an invalid program.] 
    MyImplementationController.Hello1() +0 

[TargetInvocationException: Exception has been thrown by the target of an invocation.] 
    System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) +0 
    System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) +192 
    System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +155 
    System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) +19 

をそして、これは私のコードです:

 foreach (var methodInfo in methodInfosFromSomewhere) 
     { 
      var arguments = methodInfo.GetParameters().Select(a => a.ParameterType).ToArray(); 
      MethodBuilder mb = tb.DefineMethod(
       methodInfo.Name, 
       MethodAttributes.Final | MethodAttributes.Public, 
       CallingConventions.HasThis, 
       methodInfo.ReturnType, 
       arguments); 

      // method 
      ILGenerator il = mb.GetILGenerator(); 
      int numParams = arguments.Length; 
      for (byte x = 0; x < numParams; x++) 
      { 
       //il.Emit(OpCodes.Ldarg_S, x); 
       il.Emit(OpCodes.Ldstr, x); 
      } 
      //il.EmitCall(OpCodes.Call, methodInfo, arguments); 
      il.Emit(OpCodes.Call, methodInfo); 

      il.Emit(OpCodes.Ret); 
     } 

@Edit

をどこに問題がある。最後に、私は(おそらく)を知っています!私がEmit.Callと呼ぶとき、私はこのオブジェクトのメソッドを呼びたくありません。メソッドを別のオブジェクトから呼び出す必要があります。

この例を見てみてください:

// this is interface that we want to 'decorate' 
public interface IMyInterface 
{ 
    MyResponse Hello1(); 
    MyResponse Hello2(MyRequest request); 
    MyResponse Hello3(MyRequest request); 
} 
public class MyImplementation : IMyInterface 
{ 
    public MyResponse Hello1() 
    { 
     return new MyResponse { Name = "empty" }; 
    } 
    // ... rest of implementation, it doesn't matter 
} 

私が生成するクラスがそのようsmthingされています。あなたは私が他のオブジェクトからメソッドを呼び出したい見ることができるように

public class GeneratedClass : ApiController 
{ 
    public MyInterface myImplementation { get; set; } 
    public MyResponse Hello1() 
    { 
     return myImplementation.Hello1(); 
    } 
    // ... rest of implementation, it doesn't matter 
} 

。私は、オブジェクトのプロパティを作成する方法を知っているが、私はソースから他のオブジェクト

+0

命令は、引数ではなく、バイトとして文字列を期待しています。 –

+0

メソッドの引数型を事前に知っていますか?完全に動的な方法で呼び出しを生成することは、かすかな心のためではありません。各パラメーターをロードするための正しい(型に安全な)コードを発行する必要があります。 –

+0

保存されているところに '' 'MethodInfo'''があります。私は何も知らない – MAGx2

答えて

1

からメソッドを呼び出す方法がわからない:OpCode場合(http://referencesource.microsoft.com/#mscorlib/system/reflection/emit/ilgenerator.cs,3e110f4a19d1c05e

public virtual void Emit(OpCode opcode, MethodInfo meth) 
{ 
    //... 
    if (opcode.Equals(OpCodes.Call) || opcode.Equals(OpCodes.Callvirt) || opcode.Equals(OpCodes.Newobj)) 
    { 
     EmitCall(opcode, meth, null); 
    } 
    else 
    { 
     // ... 
    } 
} 

あなたが見ることができるように、Emit()通話EmitCall() ,、またはNewobjなので、Emit()EmitCall()の間に違いはありません。

OpCodes.Ldstrで出力すると、stringのオペランドが必要です。何をしたいのですか?OpCodes.Call命令を発行する前に、スタックに引数を1つずつロードしてください。

の代わりに:質問更新後

switch (numParams) 
{ 
    case 0: 
     break; 
    case 1: 
     il.Emit(OpCodes.Ldarg_0); 
     break; 
    case 2: 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Ldarg_1); 
     break; 
    case 3: 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Ldarg_1); 
     il.Emit(OpCodes.Ldarg_2); 
     break; 
    default: 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Ldarg_1); 
     il.Emit(OpCodes.Ldarg_2); 
     il.Emit(OpCodes.Ldarg_3); 
     for (int i = 4; i < numParams; i++) 
     { 
      il.Emit(OpCodes.Ldarg, mb.GetParameters()[i]); 
     } 
     break; 
} 

編集: あなたの新しいタイプでプロパティmyImplementationを定義する必要があります

for (byte x = 0; x < numParams; x++) 
{ 
    il.Emit(OpCodes.Ldstr, x); 
} 

これを試してみてください。

これを試してみてください:オペコードOpCodes.Ldstrと

// Create field to back your "myImplementation" property 
FieldBuilder newBackingField = tb.DefineField("backingField_myImplementation", typeof(MyInterface), System.Reflection.FieldAttributes.Private); 
// Create your "myImplementation" property 
PropertyBuilder newProp = tb.DefineProperty("myImplementation", System.Reflection.PropertyAttributes.None, typeof(MyInterface), Type.EmptyTypes); 
// Create get-method for your property 
MethodBuilder getter = tb.DefineMethod("get_myImplementation", System.Reflection.MethodAttributes.Private); 
ILGenerator getterILGen = getter.GetILGenerator(); 
// Basic implementation (return backing field value) 
getterILGen.Emit(OpCodes.Ldarg_0); 
getterILGen.Emit(OpCodes.Ldfld, newBackingField); 
getterILGen.Emit(OpCodes.Ret); 

// Create set-method for your property 
MethodBuilder setter = tb.DefineMethod("set_myImplementation", System.Reflection.MethodAttributes.Private); 
setter.DefineParameter(1, System.Reflection.ParameterAttributes.None, "value"); 
ILGenerator setterILGen = setter.GetILGenerator(); 
// Basic implementation (set backing field) 
setterILGen.Emit(OpCodes.Ldarg_0); 
setterILGen.Emit(OpCodes.Ldarg_1); 
setterILGen.Emit(OpCodes.Stfld, newBackingField); 
setterILGen.Emit(OpCodes.Ret); 

// Hello1 Method 
MethodBuilder hello1 = tb.DefineMethod("Hello1", System.Reflection.MethodAttributes.Public); 
ILGenerator il = hello1.GetILGenerator(); 

// Here, add code to load arguments, if any (as shown previously in answer) 

il.Emit(OpCodes.Ldarg_0); 
il.Emit(OpCodes.Call, getter); 
il.Emit(OpCodes.Callvirt, typeof(MyInterface).GetMethod("Hello1")); 
il.Emit(OpCodes.Ret); 
+0

まだ同じを持っています。ところで、私はパラメータを取らないメソッドを使ってテストしています。 – MAGx2

+0

パラメータがない場合、新しいメソッドには2つの命令しかありません。 呼び出し(他のメソッド) Ret –

+0

通常はパラメータを持つことができます。第二に、 '' Emit'''や '' EmitCall''を使うべきですか? – MAGx2

関連する問題