2012-03-22 3 views
0

基本的には、すべての呼び出しを別のクラスにリダイレクトするラッパークラスを生成しようとしています+呼び出す前/呼び出し後にいくつかの引数/戻り値の変換を行います。しかし、il.Emit(OpCodes.Call, base_method_caller.Method)は、エラー "System.InvalidOperationException:グローバルなメソッドまたはフィールドを別のモジュールからインポートできません"で失敗しました。デリゲートが正しいです、私はo.DynamicInvoke(...)経由でそれを呼び出すことができます。誰もDelegateに連絡するEmitの指示書を書く方法を知っていますか?ILGeneratorを介した代理メソッドの呼び出しに失敗しました。「別のモジュールからグローバルメソッドまたはフィールドをインポートできません。

public class base_class 
    { 
    private inner_class _inner = new inner_class(); 
    protected inner_class wrapped { get { return _inner; } } 
    } 

public class inner_class 
    { 
    public bool do_check_value(int value) { return value > 0; } 
    } 

[TestFixture] 
public class when_wrapper_generated 
    { 
    [Test] 
    public void test_invoke_method_returns_expected() 
     { 
     var new_class = generate_class("test"); 

     var wrapper = Activator.CreateInstance(new_class); 

     var r = wrapper.GetType().GetMethod("check_value").Invoke(wrapper, new object[]{ 10 }); 

     Assert.That(r, Is.Not.Null); 
     Assert.That(r, Is.TypeOf<bool>()); 
     } 

    private static Type generate_class(string new_type_name) 
     { 
     var domain = AppDomain.CurrentDomain; 
     var assembly_name = new AssemblyName(new_type_name + "_assembly"); 
     var assembly = domain.DefineDynamicAssembly(assembly_name, AssemblyBuilderAccess.RunAndSave); 
     var module = assembly.DefineDynamicModule(new_type_name + "_module"); 

     // type 
     var base_type = typeof(base_class); 

     var type = module.DefineType(new_type_name, TypeAttributes.Public | TypeAttributes.Class, base_type); 

     // method 
     var base_method = typeof(inner_class).GetMethod("do_check_value"); 
     var base_method_caller = make_method_caller(typeof(base_class), base_method); 

     var method = type.DefineMethod("check_value", MethodAttributes.HideBySig | MethodAttributes.Public, CallingConventions.Standard, 
      base_method.ReturnType, base_method.GetParameters().Select(p => p.ParameterType).ToArray()); 

     var il = method.GetILGenerator(); 
     il.Emit(OpCodes.Ldarg_0); 
     for (var i = 0; i < base_method.GetParameters().Length; ++i) 
      il.Emit(OpCodes.Ldarg_S, i + 1); 
     il.Emit(OpCodes.Call, base_method_caller.Method); 
     il.Emit(OpCodes.Ret); 

     var result = type.CreateType(); 

     return result; 
     } 

    public static Delegate make_method_caller(Type base_type, MethodInfo method) 
     { 
     var thisParam = Expression.Parameter(base_type, "thisExp"); 

     var wrapped_property = base_type.GetProperty("wrapped", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 
     var wrapped_expr = Expression.Property(thisParam, wrapped_property); 

     var method_arguments = new List<ParameterExpression>(); 
     for (var i=0; i < method.GetParameters().Length; ++i) 
      method_arguments.Add(Expression.Parameter(method.GetParameters()[i].ParameterType, string.Format("p{0}", i+1))); 

     var call_expr = Expression.Call(wrapped_expr, method, method_arguments.Cast<Expression>().ToArray()); 

     var lambda_arguments = new List<ParameterExpression>(new[]{ thisParam }); 
     lambda_arguments.AddRange(method_arguments); 

     var d = Expression.Lambda(call_expr, lambda_arguments.ToArray()).Compile(); 
     return d; 
     } 
    } 

答えて

1

Expressionはまったく必要ありません。 ILのプロパティにアクセスして、必要なメソッドを直接呼び出すだけです。

var property = typeof(BaseClass) 
    .GetProperty("Wrapped", BindingFlags.Instance | BindingFlags.NonPublic); 

var il = method.GetILGenerator(); 
il.Emit(OpCodes.Ldarg_0); 

il.Emit(OpCodes.Call, property.GetGetMethod(true)); 

for (var i = 0; i < baseMethod.GetParameters().Length; ++i) 
    il.Emit(OpCodes.Ldarg_S, i + 1); 
il.Emit(OpCodes.Call, baseMethod); 
il.Emit(OpCodes.Ret); 

また、.Net命名規則に従うことをお勧めします。

+0

ありがとうございました!それは今私が持っているものです。私はまた、[リフレクター用のReflectionEmitLanguageアドイン](http://reflectoraddins.codeplex.com/wikipage?title=reflectionemitlanguage)が非常に便利であることも発見しました。 –