В основном я пытаюсь создать некоторый класс-обертку, который будет перенаправлять все вызовы в другой класс + выполняет некоторые преобразования аргумента / возвращаемого значения до / после вызова.Но вызов 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;
}
}