我创建了一个控制台应用程序项目(目标为.NET Core 3.0)和一个类库(目标为.NET Standard 2.0)。控制台应用程序尝试使用Roslyn编译器编译一些引用先前创建的类库的C#代码。但是我遇到了一些重大问题。
以下是控制台应用程序的代码(请注意,其中大部分是来自https://github.com/joelmartinez/dotnet-core-roslyn-sample/blob/master/Program.cs的示例代码):
我已在代码中用 //1 和 //2 进行了两处注释。第一处是引入第一个问题的那一行,导致编译失败。第二处(目前被注释掉)试图通过添加对 netstandard.dll 文件的引用来解决第一个问题(抱歉路径可能不可移植,只是我在我的机器上找到它的地方),但没有解决任何问题,只引入了更加晦涩的错误。
你有什么想法或建议可以让这段代码工作起来?
以下是控制台应用程序的代码(请注意,其中大部分是来自https://github.com/joelmartinez/dotnet-core-roslyn-sample/blob/master/Program.cs的示例代码):
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp; //nuget Microsoft.CodeAnalysis.CSharp
using Microsoft.CodeAnalysis.Emit;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
//This is a class library I made in a separate project in the solution and added as a reference to the console application project.
//The important bit for the reproduction of the issue is that ClassLibrary1 targets .NET Standard 2.0.
using ClassLibary1;
namespace RoslynIssueRepro
{
class Program
{
static void Main(string[] args)
{
string codeToCompile =
@"
using ClassLibary1;
using System;
namespace RoslynCompileSample
{
public class Writer
{
public void Execute()
{
//1. this next line of code introduces the issue during Roslyn compilation (comment it out and everything runs fine).
//It causes the code to reference a .NET Standard 2.0 class library (and this console app targets .NET Core 3.0).
//Note: If the referenced class library targets .NET Core 3.0, everything works fine.
//The error looks like this:
// CS0012: The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'.
Console.WriteLine(Class1.DoStuff());
Console.WriteLine(""Freshly compiled code execution done!"");
}
}
}";
var refPaths = new[] {
typeof(System.Object).GetTypeInfo().Assembly.Location,
typeof(Console).GetTypeInfo().Assembly.Location,
Path.Combine(Path.GetDirectoryName(typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly.Location), "System.Runtime.dll"),
typeof(Class1).GetTypeInfo().Assembly.Location,
//2. So adding a reference to netstandard.dll to alleviate the issue does not work.
//Instead it causes even more compilation errors of this form:
// CS0518: Predefined type 'System.Object' is not defined or imported
// CS0433: The type 'Console' exists in both 'System.Console, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' and 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'
//Go ahead and try it by uncommenting the line below:
//Environment.ExpandEnvironmentVariables(@"C:\Users\%USERNAME%\.nuget\packages\netstandard.library\2.0.0\build\netstandard2.0\ref\netstandard.dll")
};
RoslynCompileAndExecute(codeToCompile, refPaths);
}
#region example code from https://github.com/joelmartinez/dotnet-core-roslyn-sample/blob/master/Program.cs
private static void RoslynCompileAndExecute(string codeToCompile, string[] refPaths)
{
Write("Let's compile!");
Write("Parsing the code into the SyntaxTree");
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(codeToCompile);
string assemblyName = Path.GetRandomFileName();
MetadataReference[] references = refPaths.Select(r => MetadataReference.CreateFromFile(r)).ToArray();
Write("Adding the following references");
foreach (var r in refPaths)
Write(r);
Write("Compiling ...");
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)
{
Write("Compilation failed!");
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
foreach (Diagnostic diagnostic in failures)
{
Console.Error.WriteLine("\t{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
}
}
else
{
Write("Compilation successful! Now instantiating and executing the code ...");
ms.Seek(0, SeekOrigin.Begin);
Assembly assembly = AssemblyLoadContext.Default.LoadFromStream(ms);
var type = assembly.GetType("RoslynCompileSample.Writer");
var instance = assembly.CreateInstance("RoslynCompileSample.Writer");
var meth = type.GetMember("Execute").First() as MethodInfo;
meth.Invoke(instance, null);
}
}
}
static Action<string> Write = Console.WriteLine;
#endregion
}
}
而 ClassLibrary1 的代码只是这样的:
namespace ClassLibary1
{
public static class Class1
{
public static string DoStuff()
{
return "asdfjkl";
}
}
}
我已在代码中用 //1 和 //2 进行了两处注释。第一处是引入第一个问题的那一行,导致编译失败。第二处(目前被注释掉)试图通过添加对 netstandard.dll 文件的引用来解决第一个问题(抱歉路径可能不可移植,只是我在我的机器上找到它的地方),但没有解决任何问题,只引入了更加晦涩的错误。
你有什么想法或建议可以让这段代码工作起来?
@"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.0.0\ref\netcoreapp3.0\netstandard.dll"
,而不是原始代码中涉及“.nuget”的路径。此时,它能够进行动态编译并运行代码。谢谢!有没有更“便携”的方法可以编写该代码(这样我就不必引用某个硬编码的路径上的dll)? - Anssssssnetcore30
的情况下,要解析path_to_dotnet_sdks\packs\Microsoft.NETCore.App.Ref\3.0.0\ref\netcoreapp3.0\
,您可以使用 https://github.com/dotnet/sdk/blob/master/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.FrameworkReferenceResolution.targets#L274。此外,对于.netframework461-472
,您应该使用Microsoft.NET.Build.Extensions.NETFramework.targets
及其相关目标等等。 - George Alexandria.proj
文件或.sln
文件,您可以查看https://github.com/dotnet/roslyn/blob/master/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs。在某些情况下,它可以避免手动解析。 - George Alexandria