2

基本的に静的クラスのいくつかのメソッドをラップする動的型を作成しようとしています。Wrapという静的メソッドを作成しました。このメソッドには、インターフェイスである必要のあるジェネリックパラメーターと、静的メソッドを持つクラスのタイプである1つの通常のパラメーターがあります。

例えば

IInterfaceTest obj = StaticInterface.Wrap<IInterfaceTest>(typeof(StaticClassNameHere));
obj.TestInterfaceMethod();

しかし、メソッドを呼び出すと、InvalidProgramExceptionが発生するため、生成しているコードは明らかにどこかで壊れています。

私は自分が作成したテストクラスのILDasm出力に基づいてコードを作成しましたが、私が知る限り、同じコードを出力しています。しかし、それは機能していません...

public static class StaticInterface
{
    private static AssemblyBuilder _asm = null;
    private static ModuleBuilder _mod = null;
    private static Type _thisType = typeof(StaticInterface);
    private static int _count = 0;
    public static T Wrap<T>(Type type)
    {
      ILGenerator ilgen;

      if (_asm == null)
      {
         _asm = AppDomain.CurrentDomain.DefineDynamicAssembly(new System.Reflection.AssemblyName(_thisType.Name), AssemblyBuilderAccess.Run);
         _mod = _asm.DefineDynamicModule(_thisType.Name);
      }

      string newTypeName = _thisType.Name + "._" + _count++;

      TypeBuilder typBuilder = _mod.DefineType(newTypeName, System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Public);
      typBuilder.AddInterfaceImplementation(typeof(T));

      ConstructorBuilder conBuilder = typBuilder.DefineDefaultConstructor(System.Reflection.MethodAttributes.Public);

      foreach (MethodInfo method in typeof(T).GetMethods())
      {
         ParameterInfo[] parameters = method.GetParameters();
         Type[] paramTypes = new Type[parameters.Length];

         for (int j = 0; j < parameters.Length; j++)
         {
            paramTypes[j] = parameters[j].ParameterType;
         }

         MethodBuilder mth = typBuilder.DefineMethod(method.Name,
            MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Final,
                        method.ReturnType,
                        paramTypes);

          ilgen = mth.GetILGenerator();
          ilgen.Emit(OpCodes.Nop);

          for (short j = 0; j < parameters.Length; j++)
          {
             ilgen.Emit(OpCodes.Ldarg, j + 1);
          }

          MethodInfo callMeth = type.GetMethod(method.Name, BindingFlags.Public | BindingFlags.Static, null, paramTypes, null);

          ilgen.EmitCall(OpCodes.Call, callMeth, null);

          if (method.ReturnType != null && method.ReturnType != typeof(void))
          {
             ilgen.Emit(OpCodes.Stloc_0);
             Label end = ilgen.DefineLabel();
             ilgen.Emit(OpCodes.Br_S, end);

             ilgen.MarkLabel(end);
             ilgen.Emit(OpCodes.Ldloc_0);
          }

          ilgen.Emit(OpCodes.Ret);
       }

       typBuilder.CreateType();

       return (T)_asm.CreateInstance(newTypeName, false, BindingFlags.Public | BindingFlags.Instance, null, null, null, null);
    }
}
4

1 に答える 1

3

主に、返品タイプの取り扱いに関連していると思います。

ilgen.EmitCall(OpCodes.Call, callMeth, null);
if (method.ReturnType != null && method.ReturnType != typeof(void))
{
    ilgen.Emit(OpCodes.Stloc_0);
    Label end = ilgen.DefineLabel();
    ilgen.Emit(OpCodes.Br_S, end);

    ilgen.MarkLabel(end);
    ilgen.Emit(OpCodes.Ldloc_0);
}
ilgen.Emit(OpCodes.Ret);
  • あなたは地元の人を定義していないのでStloc_0Ldloc_0違法です
  • 分岐は...次の行に分岐するだけなので(何もしません)、これは基本的に「Xに格納し、Xからロードする」です。これは、割り当ての副作用を気にしない場合は何もしません。バツ
  • これはとにかく完全に不要です-一致する必要があるため、スタックに戻り値を残すだけです(つまり、メソッドが値を返さない場合は問題ありません。を返す場合、その値はすでにスタック上の値-つまり、まさに私たちが望むもの)

したがって、より単純な(そして機能する)実装は次のとおりです。

ilgen.EmitCall(OpCodes.Call, callMeth, null);
ilgen.Emit(OpCodes.Ret);
于 2012-08-30T11:25:35.997 に答える