在 .NET Core 1.0 中运行时编译和 运行 代码
Compiling and running code at runtime in .NET Core 1.0
是否可以在 运行 新的 .NET Core(更好的 .NET 标准平台)中编译和 运行 C# 代码?
我看过一些示例(.NET Framework),但它们使用的 NuGet 包与 netcoreapp1.0 (.NETCoreApp,Version=v1.0)
选项 #1:使用完整的 C# 编译器编译程序集,加载它,然后从中执行方法。
这需要以下包作为您 project.json 中的依赖项:
"Microsoft.CodeAnalysis.CSharp": "1.3.0-beta1-20160429-01",
"System.Runtime.Loader": "4.0.0-rc2-24027",
然后你可以使用这样的代码:
var compilation = CSharpCompilation.Create("a")
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location))
.AddSyntaxTrees(CSharpSyntaxTree.ParseText(
@"
using System;
public static class C
{
public static void M()
{
Console.WriteLine(""Hello Roslyn."");
}
}"));
var fileName = "a.dll";
compilation.Emit(fileName);
var a = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(fileName));
a.GetType("C").GetMethod("M").Invoke(null, null);
选项 #2:使用 Roslyn 脚本。这将导致代码更简单,但目前需要更多设置:
创建 NuGet.config 以从 Roslyn 夜间提要获取包:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="Roslyn Nightly" value="https://www.myget.org/F/roslyn-nightly/api/v3/index.json" />
</packageSources>
</configuration>
将以下包作为依赖添加到project.json(注意这是今天的包,以后需要不同的版本):
"Microsoft.CodeAnalysis.CSharp.Scripting": "1.3.0-beta1-20160530-01",
您还需要导入 dotnet
(过时的“Target Framework Moniker”,which is nevertheless still used by Roslyn):
"frameworks": {
"netcoreapp1.0": {
"imports": "dotnet5.6"
}
}
现在终于可以使用脚本了:
CSharpScript.EvaluateAsync(@"using System;Console.WriteLine(""Hello Roslyn."");").Wait();
我只是添加到 。如果您想将程序集保存在内存中(而不是写入文件),您可以使用以下方法:
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromStream(ms);
这与 .NET 4.5.1 中的代码不同:
Assembly assembly = Assembly.Load(ms.ToArray());
我的代码同时针对 .NET 4.5.1 和 .NET Standard,因此我不得不使用指令来解决这个问题。完整的代码示例在这里:
string code = CreateFunctionCode();
var syntaxTree = CSharpSyntaxTree.ParseText(code);
MetadataReference[] references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(Hashtable).GetTypeInfo().Assembly.Location)
};
var compilation = CSharpCompilation.Create("Function.dll",
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
StringBuilder message = new StringBuilder();
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)
{
message.AppendFormat("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
}
return new ReturnValue<MethodInfo>(false, "The following compile errors were encountered: " + message.ToString(), null);
}
else
{
ms.Seek(0, SeekOrigin.Begin);
#if NET451
Assembly assembly = Assembly.Load(ms.ToArray());
#else
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromStream(ms);
#endif
Type mappingFunction = assembly.GetType("Program");
_functionMethod = mappingFunction.GetMethod("CustomFunction");
_resetMethod = mappingFunction.GetMethod("Reset");
}
}
之前的两个答案在 Windows 上的 .NET Core 2.2 环境中对我都不起作用。需要更多参考资料。
所以在 解决方案的帮助下,我得到了这段代码:
var dotnetCoreDirectory = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
var compilation = CSharpCompilation.Create("LibraryName")
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(Console).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(Path.Combine(dotnetCoreDirectory, "mscorlib.dll")),
MetadataReference.CreateFromFile(Path.Combine(dotnetCoreDirectory, "netstandard.dll")),
MetadataReference.CreateFromFile(Path.Combine(dotnetCoreDirectory, "System.Runtime.dll")))
.AddSyntaxTrees(CSharpSyntaxTree.ParseText(
@"public static class ClassName
{
public static void MethodName() => System.Console.WriteLine(""Hello C# Compilation."");
}"));
// Debug output. In case your environment is different it may show some messages.
foreach (var compilerMessage in compilation.GetDiagnostics())
Console.WriteLine(compilerMessage);
比输出库到文件:
var fileName = "LibraryName.dll";
var emitResult = compilation.Emit(fileName);
if (emitResult.Success)
{
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(fileName));
assembly.GetType("ClassName").GetMethod("MethodName").Invoke(null, null);
}
或内存流:
using (var memoryStream = new MemoryStream())
{
var emitResult = compilation.Emit(memoryStream);
if (emitResult.Success)
{
memoryStream.Seek(0, SeekOrigin.Begin);
var context = AssemblyLoadContext.Default;
var assembly = context.LoadFromStream(memoryStream);
assembly.GetType("ClassName").GetMethod("MethodName").Invoke(null, null);
}
}
是否可以在 运行 新的 .NET Core(更好的 .NET 标准平台)中编译和 运行 C# 代码?
我看过一些示例(.NET Framework),但它们使用的 NuGet 包与 netcoreapp1.0 (.NETCoreApp,Version=v1.0)
选项 #1:使用完整的 C# 编译器编译程序集,加载它,然后从中执行方法。
这需要以下包作为您 project.json 中的依赖项:
"Microsoft.CodeAnalysis.CSharp": "1.3.0-beta1-20160429-01",
"System.Runtime.Loader": "4.0.0-rc2-24027",
然后你可以使用这样的代码:
var compilation = CSharpCompilation.Create("a")
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location))
.AddSyntaxTrees(CSharpSyntaxTree.ParseText(
@"
using System;
public static class C
{
public static void M()
{
Console.WriteLine(""Hello Roslyn."");
}
}"));
var fileName = "a.dll";
compilation.Emit(fileName);
var a = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(fileName));
a.GetType("C").GetMethod("M").Invoke(null, null);
选项 #2:使用 Roslyn 脚本。这将导致代码更简单,但目前需要更多设置:
创建 NuGet.config 以从 Roslyn 夜间提要获取包:
<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <add key="Roslyn Nightly" value="https://www.myget.org/F/roslyn-nightly/api/v3/index.json" /> </packageSources> </configuration>
将以下包作为依赖添加到project.json(注意这是今天的包,以后需要不同的版本):
"Microsoft.CodeAnalysis.CSharp.Scripting": "1.3.0-beta1-20160530-01",
您还需要导入
dotnet
(过时的“Target Framework Moniker”,which is nevertheless still used by Roslyn):"frameworks": { "netcoreapp1.0": { "imports": "dotnet5.6" } }
现在终于可以使用脚本了:
CSharpScript.EvaluateAsync(@"using System;Console.WriteLine(""Hello Roslyn."");").Wait();
我只是添加到
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromStream(ms);
这与 .NET 4.5.1 中的代码不同:
Assembly assembly = Assembly.Load(ms.ToArray());
我的代码同时针对 .NET 4.5.1 和 .NET Standard,因此我不得不使用指令来解决这个问题。完整的代码示例在这里:
string code = CreateFunctionCode();
var syntaxTree = CSharpSyntaxTree.ParseText(code);
MetadataReference[] references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(Hashtable).GetTypeInfo().Assembly.Location)
};
var compilation = CSharpCompilation.Create("Function.dll",
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
StringBuilder message = new StringBuilder();
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)
{
message.AppendFormat("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
}
return new ReturnValue<MethodInfo>(false, "The following compile errors were encountered: " + message.ToString(), null);
}
else
{
ms.Seek(0, SeekOrigin.Begin);
#if NET451
Assembly assembly = Assembly.Load(ms.ToArray());
#else
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromStream(ms);
#endif
Type mappingFunction = assembly.GetType("Program");
_functionMethod = mappingFunction.GetMethod("CustomFunction");
_resetMethod = mappingFunction.GetMethod("Reset");
}
}
之前的两个答案在 Windows 上的 .NET Core 2.2 环境中对我都不起作用。需要更多参考资料。
所以在
var dotnetCoreDirectory = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
var compilation = CSharpCompilation.Create("LibraryName")
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(Console).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(Path.Combine(dotnetCoreDirectory, "mscorlib.dll")),
MetadataReference.CreateFromFile(Path.Combine(dotnetCoreDirectory, "netstandard.dll")),
MetadataReference.CreateFromFile(Path.Combine(dotnetCoreDirectory, "System.Runtime.dll")))
.AddSyntaxTrees(CSharpSyntaxTree.ParseText(
@"public static class ClassName
{
public static void MethodName() => System.Console.WriteLine(""Hello C# Compilation."");
}"));
// Debug output. In case your environment is different it may show some messages.
foreach (var compilerMessage in compilation.GetDiagnostics())
Console.WriteLine(compilerMessage);
比输出库到文件:
var fileName = "LibraryName.dll";
var emitResult = compilation.Emit(fileName);
if (emitResult.Success)
{
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(fileName));
assembly.GetType("ClassName").GetMethod("MethodName").Invoke(null, null);
}
或内存流:
using (var memoryStream = new MemoryStream())
{
var emitResult = compilation.Emit(memoryStream);
if (emitResult.Success)
{
memoryStream.Seek(0, SeekOrigin.Begin);
var context = AssemblyLoadContext.Default;
var assembly = context.LoadFromStream(memoryStream);
assembly.GetType("ClassName").GetMethod("MethodName").Invoke(null, null);
}
}