[英]Use Reflection.Emit to override a method
我正在使用System.Reflection.Emit
为抽象基础 class 生成代理类型的工具。 现在的最终要求是能够通过提供Func
来覆盖虚拟/抽象方法,并允许代理调用该Func
保留原始调用的 object 上下文。 例如,给定一个简单的类型,如:
public class A
{
public string Name { get; set; }
public virtual int NameLen() => 42;
}
我希望能够提供一个Func<A, int>
来覆盖NameLen
。 我能够很容易地生成基本代理,但我不能完全使覆盖工作。 这是一个最小的示例,代理上面的 class:
static Type GetProxyFor(Type type, Func<A, int> oride, string methodName)
{
// basic type setup
var assmName = new AssemblyName("AproxyAssm");
var assmBuilder = AssemblyBuilder.DefineDynamicAssembly(assmName, AssemblyBuilderAccess.Run);
var moduleBuilder = assmBuilder.DefineDynamicModule(assmName.Name);
var typeBuilder = moduleBuilder.DefineType("AProxy", TypeAttributes.Public, type);
// create the override method
var mInfo = typeBuilder.BaseType.GetMethod(methodName);
var mBody = typeBuilder.DefineMethod(mInfo.Name,
MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.Virtual | MethodAttributes.HideBySig,
mInfo.ReturnType, null);
// call the Func
var ilGen = mBody.GetILGenerator();
ilGen.Emit(OpCodes.Nop);
ilGen.Emit(OpCodes.Ldsfld, oride.Target.GetType().GetFields()[0]); // ???
ilGen.Emit(OpCodes.Ldarg_0); // load method obj context as func arg 1
ilGen.Emit(OpCodes.Callvirt, oride.Method);
ilGen.Emit(OpCodes.Ret);
typeBuilder.CreateTypeInfo();
var rettype = typeBuilder.CreateType();
return rettype;
}
生成的 IL 似乎是正确的,因为我没有收到任何 JIT 错误或旧的最喜欢的CLR detected an invalid program
,并且代理 class 以其他方式工作,即使对于更复杂的示例也是如此。 只要,就是因为我不调用被覆盖的方法。 当我调用该方法时,我得到的是System.FieldAccessException: Attempt by method 'AProxy.NameLen()' to access field 'EmitTests.OverrideTests+<>c.<>9' failed. ' failed.
System.FieldAccessException: Attempt by method 'AProxy.NameLen()' to access field 'EmitTests.OverrideTests+<>c.<>9' failed. ' failed.
结合以上所有内容的单元测试:
[Fact]
public void CanEmitOverrideMethod()
{
Func<A, int> f = (t) => t.Name.Length;
var t = GetProxyFor(typeof(A), f, "NameLen");
var obj = Activator.CreateInstance(t);
Assert.IsAssignableFrom<A>(obj);
((A)obj).Name = "Foo";
Assert.Equal(3, ((A)obj).NameLen());
}
更新了更简洁的示例。 此外,在检查生成的<>c
class 时,这里是来自 ILSpy 的 output:
// EmitTests.OverrideTests.<>c
using System;
using System.Runtime.CompilerServices;
[Serializable]
[CompilerGenerated]
private sealed class <>c
{
public static readonly <>c <>9 = new <>c();
public static Func<A, int> <>9__1_0;
internal int <CanEmitOverrideMethod>b__1_0(A t)
{
return t.Name.Length;
}
}
对我来说,这一切看起来都不错,但我有点不知道接下来去哪里 go。
这个答案并不像我使用Emit
覆盖的目标那样聪明或有趣,但它 a) 有效并且 b) 可能更易于维护。 创建代理类型时,我使用 static 字典注册 func,然后在运行时进行查找。 一些快速而肮脏的基准测试表明查找基本上是免费的。 调用没有注册函数的实例与调用超过 100 万次重复的常规方法一样快。 注册一个 func 会有一点点开销,但在相同的 1m 次重复中,它只会增加几分之一秒。 开销似乎是在实际调用 Func 时,它可能也存在于上面的 Emit() 方法中。
public class A
{
public string Name { get; set; }
public virtual int NameLen()
{
var t = this.GetType();
if (funcs.ContainsKey(t))
{
return funcs[t](this);
}
else
{
return 42;
}
}
public static Dictionary<Type, Func<A, int>> funcs = new Dictionary<Type, Func<A, int>>();
}
和代理生成器:
static Type GetProxyFor<T>(Func<A, int> oride) where T : A
{
// basic type setup
var assmName = new AssemblyName("AproxyAssm");
var assmBuilder = AssemblyBuilder.DefineDynamicAssembly(assmName, AssemblyBuilderAccess.Run);
var moduleBuilder = assmBuilder.DefineDynamicModule(assmName.Name);
var typeBuilder = moduleBuilder.DefineType("AProxy", TypeAttributes.Public, typeof(T));
typeBuilder.CreateTypeInfo();
var rettype = typeBuilder.CreateType();
if (oride != null)
{
A.funcs.Add(rettype, oride);
}
return rettype;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.