使用 .net core 3.0 动态编译和运行代码进行脚本编写 [英] Dynamically compile and run code using .net core 3.0 for scripting
问题描述
我想提供在 .NET core 3 中编译和运行代码(Csharp 类)的可能性,以便编写脚本.脚本(类)应从文件系统加载并注入现有(静态)程序集中.https://laurentkempe.com/2019/02/18/dynamically-compile-and-run-code-using-dotNET-Core-3.0/(使用 AssemblyContext)似乎是一种有效的方法.
I want to offer the possibly to compile and run code (Csharp Classes) in .NET core 3 for purpose of scripting. The scripts (classes) shall be loaded from the file system and injected in an existing (static) assembly. https://laurentkempe.com/2019/02/18/dynamically-compile-and-run-code-using-dotNET-Core-3.0/ (using AssemblyContext) seems to be a valid approach for this.
如果我不需要在程序集中隔离脚本代码,是否有更简单的解决方案(开销更少)?.(调试应该是可以的)
Is there a simpler solution (with less overhead) if I do not have the need to isolate the script code in an assembly?. (Debugging should be possible)
推荐答案
https://laurentkempe.com/2019/02/18/dynamically-compile-and-run-code-using-dotNET-Core-3.0/.
为了节省时间,这里有一个运行单个文件、支持 LINQ 和来自当前项目 DLL 的类的程序的变体:
To save time, here is a variant of the program that runs a single file, supports LINQ, and classes from current project DLL :
程序.cs
using System.Linq;
using DynamicRun.Builder;
namespace DynamicRun
{
class Program
{
static void Main(string[] args)
{
var compiler = new Compiler();
var runner = new Runner();
byte[] compiled = compiler.Compile(args.FirstOrDefault());
runner.Execute(compiled, args[1..args.Length]);
}
}
}
编译器.cs
using System;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
namespace DynamicRun.Builder
{
internal class Compiler
{
public byte[] Compile(string filepath)
{
var sourceCode = File.ReadAllText(filepath);
using (var peStream = new MemoryStream())
{
var result = GenerateCode(sourceCode).Emit(peStream);
if (!result.Success)
{
Console.WriteLine("Compilation done with error.");
var failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error);
foreach (var diagnostic in failures)
{
Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
}
return null;
}
Console.WriteLine("Compilation done without any error.");
peStream.Seek(0, SeekOrigin.Begin);
return peStream.ToArray();
}
}
private static CSharpCompilation GenerateCode(string sourceCode)
{
var codeString = SourceText.From(sourceCode);
var options = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7_3);
var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(codeString, options);
var references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Linq.Enumerable).Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Runtime.AssemblyTargetedPatchBandAttribute).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo).Assembly.Location),
// Todo : to load current dll in the compilation context
MetadataReference.CreateFromFile(typeof(Family.Startup).Assembly.Location),
};
return CSharpCompilation.Create("Hello.dll",
new[] { parsedSyntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.ConsoleApplication,
optimizationLevel: OptimizationLevel.Release,
assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default));
}
}
}
Runner.cs
using System;
using System.IO;
using System.Runtime.CompilerServices;
namespace DynamicRun.Builder
{
internal class Runner
{
public void Execute(byte[] compiledAssembly, string[] args)
{
var assemblyLoadContext = LoadAndExecute(compiledAssembly, args);
for (var i = 0; i < 8 && assemblyLoadContext.IsAlive; i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
Console.WriteLine(assemblyLoadContext.IsAlive ? "Unloading failed!" : "Unloading success!");
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static WeakReference LoadAndExecute(byte[] compiledAssembly, string[] args)
{
using (var asm = new MemoryStream(compiledAssembly))
{
var assemblyLoadContext = new SimpleUnloadableAssemblyLoadContext();
var assembly = assemblyLoadContext.LoadFromStream(asm);
var entry = assembly.EntryPoint;
_ = entry != null && entry.GetParameters().Length > 0
? entry.Invoke(null, new object[] {args})
: entry.Invoke(null, null);
assemblyLoadContext.Unload();
return new WeakReference(assemblyLoadContext);
}
}
}
}
这篇关于使用 .net core 3.0 动态编译和运行代码进行脚本编写的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!