基本上,我正在尝试生成一些包装类,它将所有调用重定向到另一个类,并在调用之前/之后进行一些参数/返回值转换。但调用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命名约定使用。