C# Roslyn能否用于生成类似于DynamicMethod IL生成的动态方法

C# Roslyn能否用于生成类似于DynamicMethod IL生成的动态方法,c#,.net,compilation,roslyn,reflection.emit,C#,.net,Compilation,Roslyn,Reflection.emit,我一直在使用DynamicMethod生成IL,使用 method.GetILGenerator(); 这很好,但当然很难使用,因为您通常不希望在像C#这样的高级语言中使用低级IL。既然有罗斯林,我想我可以用它来代替。我试图找出如何使用Roslyn做类似的事情:生成一个动态方法,然后为它创建一个委托。我能做到这一点的唯一办法就是像这样上完整的课 SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(@" using System; namesp

我一直在使用DynamicMethod生成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做一些类似于DynamicMethod的事情,这样我就只能定义一个附加到类型的方法体?如果没有,编译许多动态程序集是否有什么大的缺点(比如太多的程序集无法加载等等)

您可以使用
CSharpScript
类<代码>等待CSharpScript.EvaluateAsync(“1+2”)只计算表达式。您可以在Microsoft.CodeAnalysis.Scripting.CSharp软件包(当前仅为预发布版本)中找到它。使用ScriptOptions(第二个参数)添加使用和程序集引用

编译要委托的表达式:

var func = CSharpScript.Create<int>("1 + 3").CompileToDelegate()
var func=CSharpScript.Create(“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 });
等待CSharpScript.Create(“1+x”,
ScriptOptions.Default.AddReferences(typeof(Program.Assembly)),
globalsType:typeof(ScriptGlobals))
.CreateDelegate()
.Invoke(新的ScriptGlobals(){x=4});


我还有一个办法解决你的问题,那就是根本不用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
var parameter=Expression.parameter(typeof(int),“a”);//定义参数
var body=Expression.Add(参数,Expression.Constant(42));//求和参数和数
var lambdaExpression=Expression.Lambda(新[]{parameter},body);//定义方法
var add42Delegate=lambdaExpression.Compile();//编译为动态方法

你几乎可以用它做任何事情,它比ILGenerator舒适得多,并且包含在标准库中。

我想用
表达式
函数
来评论exyi的答案,但我没有足够的声誉。所以我的“答案”来了

如果您所需要的只是一段可以使用参数执行的一流公民代码,那么您可以简单地创建Lambda,如下所示:

Func<int, int> add42 = number => number + 42;
// Called like this:
int theNumber46 = add42.Invoke(4);
Func add42=number=>number+42;
//这样称呼:
int theNumber46=add42.Invoke(4);
如果需要实际的表达式树,还有一个简洁的快捷方式:

Expression<Func<int, int>> add42 = number => number + 42;
// Called like this:
int theNumber46 = add42.Compile().Invoke(4);
表达式add42=number=>number+42;
//这样称呼:
int theNumber46=add42.Compile().Invoke(4);
代码中唯一的区别是,使用
表达式
包装
Func
。概念上的区别在于,Lambda(或本例中的
Func
,但也有其他Lambda)可以按原样执行,而
表达式
需要首先使用
Compile()
方法编译。但是
表达式
保存有关语法树的信息,因此可以用于EntityFramework中使用的
iQuery
数据提供程序


所以这一切都取决于你想用你的动态方法/lamda/delegate做什么。

Roslyn过去有一个
DynamicMethod
发射器,但是
DynamicMethod
有太多的限制,所以它被删除了。这是一个遗憾,因为它应该提供很好的特性,而不会干扰程序集加载……只有一个小问题。CSharpScript会暂时将内存使用量增加到1 GB左右,但大部分都是在编译后进行清理。@mswietlicki内存使用量的增加通常是由于“全局”类的大量依赖项/引用所致。我想自然的本能是在项目/dll中创建一个“全局”类型作为另一个类(我创建了),但这意味着Roslyn编译器必须加载所有这些依赖项才能构建脚本。一个巧妙的技巧是将您的“全局”类型托管在一个单独的“脚本”dll中,具有最小的外部依赖性。编译时间和内存将。。。那就自己试试看吧。Roslyn doc中的一个小细节。