当我能够动态生成方法时,我对.Net Expressions
有所了解。没关系,这很好。
但是现在我需要生成一个完整的类,似乎唯一的方法是发送整个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框架来生成一些代码。
如果我可以使用Expressions
,并使用我已经用它生成的方法创建一个类。但是现在我不知道该怎么做。
答案 0 :(得分:1)
您可以使用CodeDOM和Emit。但是,我认为这不值得。
对于类似的事情,我使用以下库: http://www.castleproject.org/projects/dynamicproxy/
即使目标类不可用,也可以创建代理类(然后必须拦截所有方法)。
答案 1 :(得分:1)
首先,由于您正在处理远程处理,我不得不提到.NET最初是为了支持而设计的(从.NET作为COM 2.0返回)。您最直接的解决方案是实现透明的远程处理代理 - 只需创建自己的(可能是通用的)类派生自System.Runtime.Remoting.Proxies.RealProxy
,并且您可以通过覆盖{{1}来提供实现所需函数所需的所有逻辑。 }} 方法。使用Invoke
,您可以获得实施界面的代理,并且您可以继续使用。
显然,在每次调用期间,这都会在运行时产生成本。但是,除了您根本不进行任何I / O之外,它通常完全不重要,特别是如果您正在处理网络。实际上,除非你处于紧密的循环中,即使不进行I / O操作也是非常不重要的 - 只有性能测试才能真正判断出你是否对成本有好处。< / p>
如果你真的想要预生成所有方法体,而不是在运行时保持逻辑动态,你可以利用GetTransparentProxy
给你LambdaExpression
的事实。与CompileToMethod
不同,你没有得到一个可以直接调用的漂亮的小委托,但它可以让你选择使用lambda表达式来显式构建方法体 - 这反过来又允许你在没有求助的情况下创建整个类委托调用。
完整(但简单)的例子:
Compile
如果您曾经使用.NET发布,那么这应该非常简单。我们定义了动态程序集,模块,类型(理想情况下,您希望在单个动态程序集中一次定义所有类型)。棘手的部分是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();
}
只支持静态方法,所以我们需要作弊。首先,我们创建一个静态方法,将Lambda.CompileToMethod
作为参数并在那里编译lamdba表达式。然后,我们创建一个方法存根 - 一个简单的IL,确保我们的静态方法被正确调用。最后,我们将接口方法绑定到存根。
在我的示例中,我假设一个无参数方法,但只要您确保this
使用与接口方法完全相同的类型,存根就像执行所有{{{1}一样简单。 1}}在一个序列中,一个LambdaExpression
和一个Ldarg
。如果你的真实代码(在静态方法中)足够短,它通常会被内联。由于Call
与其他任何参数都是一个参数,如果你感觉冒险,你可以只使用生成方法的方法体并将其直接放入虚拟方法中 - 请注意你&#39;但是,我需要两次通过。
答案 2 :(得分:1)
我目前实际上正在使用rxjs/operator
软件包,该软件包使您可以集成到MSBuild管道中并进行构建。例如,请参阅我的REST swagger或solidity -> C# codegen。