0

dotnet-scriptCSI等工具允许用户像“脚本”一样编写、编译和运行 C#,而不是将他们的代码包含在完整的预编译项目中。这些工具非常适合命令行使用,但在将动态 C#“脚本”集成到更大的 C# 应用程序方面并没有提供太多帮助。

如果我有一个现有的 C# 应用程序希望通过 .csx“脚本”将其他类加载到其现有的命名空间中,我该怎么做?可能吗?

4

1 回答 1

1

我猜你需要编译执行你的 C# 脚本。

根据我的经验,我通过直接引用Microsoft.CodeAnalysis.CSharp.Scripting(版本 3.*)来使用 C# 脚本。

<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.*" />

汇编

我建议使用默认编译选项:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Scripting;

// ...
ScriptOptions options = ScriptOptions.Default;

也许将来,您需要将引用的程序集添加到您的脚本编译中。

所以你需要编译你的script(包含在下面代码中的字符串变量中):

byte[] assemblyBinaryContent;

var roslynScript = CSharpScript.Create(script, options);
var compilation = roslynScript.GetCompilation();

compilation = compilation.WithOptions(compilation.Options
    .WithOptimizationLevel(OptimizationLevel.Release)
    .WithOutputKind(OutputKind.DynamicallyLinkedLibrary));

using (var assemblyStream = new MemoryStream())
{
    var result = compilation.Emit(assemblyStream);
    if (!result.Success)
    {
        var errors = string.Join(Environment.NewLine, result.Diagnostics.Select(x => x));
        throw new Exception("Compilation errors: " + Environment.NewLine + errors);
    }

    assemblyBinaryContent = assemblyStream.ToArray();
}

GC.Collect(); // it allows to force clear compilation stuff.

Assembly assembly = Assembly.Load(assemblyBinaryContent);

var ret = Run(assembly); // see next paragraph

执行

显然,您需要一个入口点来执行您的脚本。我发现了这个棘手的解决方案。有用。

private object Run(Assembly assembly)
{
    //Execute the script
    var type = assembly.GetType("Submission#0");
    var method = type.GetMethod("<Factory>", BindingFlags.Static | BindingFlags.Public);

    var retTask = method.Invoke(null, new object[] { new object[2] }) as Task<object>;
    return retTask.GetAwaiter().GetResult();
}

我希望它可以帮助。

于 2022-01-01T15:37:46.360 回答