通过 ILGenerator 委托方法调用失败,并显示"无法从其他模块导入全局方法或字段"。



基本上,我正在尝试生成一些包装类,它将所有调用重定向到另一个类,并在调用之前/之后进行一些参数/返回值转换。但调用il.Emit( OpCodes.Call, base_method_caller.Method )失败,错误为"System.InvalidOperationException:无法从其他模块导入全局方法或字段"。委托是正确的,我可以通过o.DynamicInvoke(...)调用它。有人知道如何编写Emit指令来调用Delegate吗?

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;
        }
    }

您根本不需要所有这些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命名约定使用。

相关内容

  • 没有找到相关文章

最新更新