📜  如何在 C# 中执行脚本(1)

📅  最后修改于: 2023-12-03 14:52:14.802000             🧑  作者: Mango

如何在 C# 中执行脚本

在开发过程中,有时需要动态执行代码。而使用脚本是一种常见的方式来实现这一需求。在 C# 中,可以通过以下几种方式来执行脚本。

1. 使用 CodeDom

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 方法。

2. 使用 Roslyn

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 更加轻量、灵活和易于使用。