0
基本上我試圖生成一些包裝類,將所有調用重定向到另一個類+調用之前/之後執行一些參數/返回值轉換。但是請致電il.Emit(OpCodes.Call, base_method_caller.Method)
失敗,並報錯「System.InvalidOperationException:無法從其他模塊導入全局方法或字段」。代表是正確的,我可以通過o.DynamicInvoke(...)
調用它。有人知道如何編寫發送指令來調用委託嗎?通過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;
}
}
謝謝!這是我現在擁有的。我還發現[ReflectionEmitLanguage反射器加載項](http://reflectoraddins.codeplex.com/wikipage?title=reflectionemitlanguage)非常有用。 – 2012-03-26 16:21:04