📅  最后修改于: 2023-12-03 14:52:14.802000             🧑  作者: Mango
在开发过程中,有时需要动态执行代码。而使用脚本是一种常见的方式来实现这一需求。在 C# 中,可以通过以下几种方式来执行脚本。
CodeDom 是 .NET Framework 提供的一种基于对象模型的代码生成和编译工具。通过 CodeDom,能够在运行时动态生成代码并编译执行。以下是使用 CodeDom 执行脚本的示例代码。
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using Microsoft.CSharp;
namespace Scripting
{
public class ScriptRunner
{
public static void RunScript(string script)
{
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add("System.dll");
parameters.GenerateInMemory = true;
CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, script);
if (results.Errors.HasErrors)
{
foreach (CompilerError error in results.Errors)
{
Console.WriteLine("Error: {0}", error.ErrorText);
}
}
else
{
Type type = results.CompiledAssembly.GetType("Script");
dynamic instance = Activator.CreateInstance(type);
instance.Execute();
}
}
}
}
以上代码中,script
参数为需要执行的脚本。CSharpCodeProvider
类是 CodeDom 框架中的一个提供器,用于编译 C# 代码。通过创建 CompilerParameters
对象来指定编译器参数,其中包括引用的程序集和是否生成所有输出到内存中。编译脚本时,通过调用 CompileAssemblyFromSource
方法来编译代码,并返回 CompilerResults
对象。在编译成功后,通过调用 GetType
方法来获取脚本中的类类型,并通过 Activator
类创建实例,再执行其 Execute
方法。
Roslyn 是 .NET Framework 中的一个开源编译器,与 CodeDom 相比,Roslyn 提供了更加灵活的代码分析和生成功能。以下是使用 Roslyn 执行脚本的示例代码。
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Scripting
{
public class ScriptRunner
{
public static void RunScript(string script)
{
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(script);
string assemblyName = Path.GetRandomFileName();
MetadataReference[] references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).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 = AssemblyLoadContext.Default.LoadFromStream(ms);
Type type = assembly.GetType("Script");
dynamic instance = Activator.CreateInstance(type);
instance.Execute();
}
}
}
}
}
以上代码中,与 CodeDom 相比,使用 Roslyn 执行脚本更加简单和直观。首先通过调用 CSharpSyntaxTree.ParseText
方法来生成代码分析器,再通过 CSharpCompilation.Create
方法来创建编译器。与 CodeDom 相比,Roslyn 的编译参数更加简单,只需指定程序集名、源代码和依赖程序集。在编译脚本后,通过 AssemblyLoadContext.Default.LoadFromStream
方法来加载并执行编译结果的程序集。
以上是在 C# 中执行脚本的两种方式,它们都能够实现动态执行代码的需求,但使用 Roslyn 相较于 CodeDom 更加轻量、灵活和易于使用。