是否有可能dynamic编译和执行C#代码片段?

我想知道是否有可能将C#代码片段保存到文本文件(或任何inputstream),然后执行这些dynamic? 假设提供给我的东西可以在任何Main()块内编译好,是否可以编译和/或执行这个代码? 我宁愿编译它出于性能的原因。

至less,我可以定义一个他们需要实现的接口,然后他们将提供一个实现该接口的代码“部分”。

在C#/所有静态.NET语言中最好的解决scheme就是使用CodeDOM来处理这些事情。 (作为一个说明,其另一个主要目的是dynamic构build代码位,甚至整个类。)

下面是LukeH博客的一个很好的例子,它使用了一些LINQ,只是为了好玩。

using System; using System.Collections.Generic; using System.Linq; using Microsoft.CSharp; using System.CodeDom.Compiler; class Program { static void Main(string[] args) { var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }); var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true); parameters.GenerateExecutable = true; CompilerResults results = csc.CompileAssemblyFromSource(parameters, @"using System.Linq; class Program { public static void Main(string[] args) { var q = from i in Enumerable.Range(1,100) where i % 2 == 0 select i; } }"); results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText)); } } 

这里最重要的类是CSharpCodeProvider ,它利用编译器即时编译代码。 如果您想要运行代码,只需要使用一点reflection来dynamic加载程序集并执行它。

这里是C#中的另一个例子(虽然稍微简洁一点)另外还显示了如何使用System.Reflection命名空间运行运行时编译的代码。

其他人已经给出了如何在运行时生成代码的很好的答案,所以我想我会解决你的第二段。 我有一些这方面的经验,只是想分享我从这个经验中学到的教训。

至less,我可以定义一个他们需要实现的接口,然后他们将提供一个实现该接口的代码“部分”。

如果您使用interface作为基本types,则可能会遇到问题。 如果将来在interface中添加一个新的方法,则现在所有实现该interface客户端提供的类都将变为抽象types,这意味着您将无法在运行时编译或实例化客户端提供的类。

在发布旧界面大约1年之后,以及在分发大量需要支持的“传统”数据之后,我需要添加新方法。 我最终创build了一个从旧的接口inheritance的新接口,但是这种方法使加载和实例化客户端提供的类变得困难,因为我必须检查哪个接口可用。

我当时想到的一个解决scheme是使用实际的类作为基本types,比如下面的类。 类本身可以被标记为抽象的,但是所有的方法都应该是空的虚拟方法(而不是抽象方法)。 然后,客户端可以重写他们想要的方法,并且可以将新方法添加到基类中,而不会使现有的客户端提供的代码失效。

 public abstract class BaseClass { public virtual void Foo1() { } public virtual bool Foo2() { return false; } ... } 

不pipe这个问题是否适用,您都应该考虑如何对您的代码库和客户端提供的代码之间的接口进行版本化。

您可以将一段C代码编译到内存中,并使用Roslyn 生成汇编字节 。 这已经提到,但值得在这里添加一些Roslyn的例子。 以下是完整的示例:

 using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Emit; namespace RoslynCompileSample { class Program { static void Main(string[] args) { SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(@" using System; namespace RoslynCompileSample { public class Writer { public void Write(string message) { Console.WriteLine(message); } } }"); string assemblyName = Path.GetRandomFileName(); MetadataReference[] references = new MetadataReference[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location) }; CSharpCompilation compilation = CSharpCompilation.Create( assemblyName, syntaxTrees: new[] { syntaxTree }, references: references, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); using (var ms = new MemoryStream()) { EmitResult result = compilation.Emit(ms); if (!result.Success) { IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error); foreach (Diagnostic diagnostic in failures) { Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage()); } } else { ms.Seek(0, SeekOrigin.Begin); Assembly assembly = Assembly.Load(ms.ToArray()); Type type = assembly.GetType("RoslynCompileSample.Writer"); object obj = Activator.CreateInstance(type); type.InvokeMember("Write", BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, new object[] { "Hello World" }); } } Console.ReadLine(); } } } 

为了编译,你可以启动一个shell调用csc编译器。 你可能会头疼,试图保持你的path和开关直,但它当然可以完成。

C#angular壳例子

编辑 :或者更好的是,使用CodeDOM作为Noldorinbuild议…

System.CodeDom.Compiler命名空间应该有所帮助。 看到这篇文章