我一直在使用DynamiMethod来生成IL
method.GetILGenerator();
这很好用,但当然很难使用,因为你通常不想在C#这样的高级语言中使用低级别的IL。现在,因为有罗斯林,我可以使用它。我试图找出如何使用Roslyn做类似的事情:生成一个动态方法,然后为它创建一个委托。我能够做到这一点的唯一方法是拥有像这样的全班
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(@"
using System;
namespace RoslynCompileSample
{
public class Writer
{
public void Write(string message)
{
Console.WriteLine(message);
}
}
}");
然后我可以使用字符串连接插入我的方法而不是Write方法。之后,在内存中生成动态程序集并加载,并使用反射来获取所需的方法并生成委托。
这个方法似乎运行正常,但对我的情况来说似乎有点过分,因为我需要使用多个独立的方法,这可能会导致大量的程序集被加载。
所以问题是:是否有一种简单的方法可以为Roslyn执行类似于动态方法的操作,这样我只能定义附加到类型的方法体?如果没有,编译许多动态程序集有什么大的缺点(比如太多无法加载等等)。
答案 0 :(得分:2)
您可以使用CSharpScript
课程。 await CSharpScript.EvaluateAsync("1 + 2")
只是评估表达式。您可以在Microsoft.CodeAnalysis.Scripting.CSharp
包中找到它(目前只有预发行版)。使用ScriptOptions(第二个参数)添加使用和程序集引用。
将表达式编译为委托:
var func = CSharpScript.Create<int>("1 + 3").CompileToDelegate()
使用globals对象将函数传递给函数:
await CSharpScript.Create<int>("1 + x",
ScriptOptions.Default.AddReferences(typeof(Program).Assembly),
globalsType: typeof(ScriptGlobals))
.CreateDelegate()
.Invoke(new ScriptGlobals() { x = 4 });
答案 1 :(得分:1)
我还有一个想法如何解决你的问题,根本不使用Roslyn。您描述使用ILGenerator
发出IL令人讨厌。但是,.NET Framework具有内置语义树,可以编译为动态方法。它们位于Linq.Expression
命名空间中,也用于Linq提供程序。
var parameter = Expression.Parameter(typeof(int), "a"); // define parameter
var body = Expression.Add(parameter, Expression.Constant(42)); // sum parameter and number
var lambdaExpression = Expression.Lambda<Func<int, int>>(new[] { parameter }, body); // define method
var add42Delegate = lambdaExpression.Compile(); // compile to dynamic method
您几乎可以使用它做任何事情,它比ILGenerator
更舒服,并且包含在标准库中。
答案 2 :(得分:0)
我想用Expression
和Func<int,int>
来评论exyi的答案,但是我没有足够的声誉。因此,这里出现了我的“答案”。
如果您需要的是可以使用参数执行的一流公民代码,则可以像下面这样简单地创建Lambda:
Func<int, int> add42 = number => number + 42;
// Called like this:
int theNumber46 = add42.Invoke(4);
如果您需要使用实际的表达式树,那么还有一个简洁的快捷方式:
Expression<Func<int, int>> add42 = number => number + 42;
// Called like this:
int theNumber46 = add42.Compile().Invoke(4);
代码的唯一区别是,您用Func<int,int>
包裹了Expression<..>
。概念上的区别在于,可以按原样执行Lambda(或本示例中的Func<>
,但也有其他Lambda),而Expression<>
首先需要与{{1 }} 方法。但是Compile()
保留了有关语法树的信息,因此可以像EntityFramework中使用的那样供Expression
数据提供者使用。
所以这一切都取决于您要对动态方法/ lamda /委托执行什么操作。