当我能够动态生成方法时,我对.Net表达有一些了解.没关系,这很好.
但现在我需要生成一个完整的类,似乎唯一的方法就是Emit整个IL,这是完全不可接受的(它是不可能支持的).
假设我们有以下界面:
public interface IFoo
{
[Description("5")]
int Bar();
[Description("true")]
bool Baz();
}
应转换为:
public class Foo : IFoo
{
public int Bar() => 5;
public bool Baz() => true;
}
我怎样才能实现它?没有第三方工具和库,它甚至可能吗?我知道GitHub上有很多有用的工具,但我真的不想导入一个完整的MVVM框架来生成一些代码.
如果我可以使用表达式,并使用我已经用它生成的方法创建一个类.但是现在我不知道该怎么做.
最佳答案 首先,由于你正在处理远程处理,我不得不提到这是.NET最初设计的基础上支持(从.NET的根源作为COM 2.0).您最直接的解决方案是实现透明的远程代理 – 只需创建自己的(可能是通用的)类派生自System.Runtime.Remoting.Proxies.RealProxy,并且您可以通过覆盖提供实现所需功能所需的所有逻辑Invoke方法.使用GetTransparentProxy,您可以获得实现您的界面的代理,并且您很高兴.
显然,在每次调用期间,这都会在运行时产生成本.但是,它通常完全不重要,因为你正在进行任何I / O,特别是如果你正在处理网络.实际上,除非你处于紧密的循环中,否则即使不进行I / O也是非常不重要的 – 只有性能测试才能真正判断出你是否对成本有好处.
如果您真的想要预生成所有方法体,而不是在运行时保持逻辑动态,则可以利用LambdaExpression为您提供CompileToMethod的事实.与Compile不同,您没有得到一个可以直接调用的好的小委托,但是它为您提供了使用lambda表达式显式构建方法体的选项 – 这反过来又允许您在不诉诸委托调用的情况下创建整个类.
一个完整(但简单)的例子:
void Main()
{
var ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.Run);
var mb = ab.DefineDynamicModule("Test");
var tb = mb.DefineType("Foo");
tb.AddInterfaceImplementation(typeof(IFoo));
foreach (var imethod in typeof(IFoo).GetMethods())
{
var valueString = ((DescriptionAttribute)imethod.GetCustomAttribute(typeof(DescriptionAttribute))).Description;
var method =
tb.DefineMethod
(
"@@" + imethod.Name,
MethodAttributes.Private | MethodAttributes.Static,
imethod.ReturnType,
new [] { tb }
);
// Needless to say, I'm making a lot of assumptions here :)
var thisParameter = Expression.Parameter(typeof(IFoo), "this");
var bodyExpression =
Expression.Lambda
(
Expression.Constant
(
Convert.ChangeType(valueString, imethod.ReturnType)
),
thisParameter
);
bodyExpression.CompileToMethod(method);
var stub =
tb.DefineMethod(imethod.Name, MethodAttributes.Public | MethodAttributes.Virtual, imethod.ReturnType, new Type[0]);
var il = stub.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, method, null);
il.Emit(OpCodes.Ret);
tb.DefineMethodOverride(stub, imethod);
}
var fooType = tb.CreateType();
var ifoo = (IFoo)Activator.CreateInstance(fooType);
Console.WriteLine(ifoo.Bar()); // 5
Console.WriteLine(ifoo.Baz()); // True
}
public interface IFoo
{
[Description("5")]
int Bar();
[Description("true")]
bool Baz();
}
如果你曾经使用.NET发射,这应该是非常简单的.我们定义了动态程序集,模块,类型(理想情况下,您希望在单个动态程序集中一次定义所有类型).棘手的部分是Lambda.CompileToMethod只支持静态方法,所以我们需要作弊.首先,我们创建一个静态方法,将其作为参数并在那里编译lamdba表达式.然后,我们创建一个方法存根 – 一个简单的IL,确保我们的静态方法被正确调用.最后,我们将接口方法绑定到存根.
在我的示例中,我假设一个无参数方法,但只要您确保LambdaExpression使用与接口方法完全相同的类型,存根就像在序列中执行所有Ldargs一样简单,一个Call和一个Ret.如果你的真实代码(在静态方法中)足够短,它通常会被内联.因为这是一个像其他任何一个的论点,如果你有冒险精神,你可以采取生成的方法的方法体,并将其直接放入虚拟方法 – 请注意你需要在两个通道中这样做但是.