【问题标题】:Using C# 6 features with CodeDomProvider (Roslyn)将 C# 6 功能与 CodeDomProvider (Roslyn) 结合使用
【发布时间】:2015-07-26 17:00:21
【问题描述】:
CodeDomProvider objCodeCompiler = CodeDomProvider.CreateProvider( "CSharp" );

CompilerParameters objCompilerParameters = new CompilerParameters();

...

CompilerResults objCompileResults = objCodeCompiler.CompileAssemblyFromFile( objCompilerParameters, files.ToArray() );

当我编译我的文件时,我得到:

FileFunctions.cs(347):错误:意外字符“$”

有谁知道如何通过 CodeDom 编译获得字符串插值?

我找到了这个链接:How to target .net 4.5 with CSharpCodeProvider?

所以我尝试了:

     var providerOptions = new Dictionary<string, string>();
     providerOptions.Add( "CompilerVersion", "v4.0" );

     // Instantiate the compiler.
     CodeDomProvider objCodeCompiler = CodeDomProvider.CreateProvider( "CSharp", providerOptions );

但我仍然遇到同样的错误。

我还将目标框架更新为 .NET Framework 4.6。

注意:我不能指定“v4.5”或“v4.6”,否则我会得到:

************** Exception Text **************
System.InvalidOperationException: Compiler executable file csc.exe cannot be found.
   at System.CodeDom.Compiler.RedistVersionInfo.GetCompilerPath(IDictionary`2 provOptions, String compilerExecutable)
   at Microsoft.CSharp.CSharpCodeGenerator.FromFileBatch(CompilerParameters options, String[] fileNames)
   at Microsoft.CSharp.CSharpCodeGenerator.System.CodeDom.Compiler.ICodeCompiler.CompileAssemblyFromFileBatch(CompilerParameters options, String[] fileNames)
   at System.CodeDom.Compiler.CodeDomProvider.CompileAssemblyFromFile(CompilerParameters options, String[] fileNames)
   at Dynamic.CodeDOMCompiler.CompileAllCodeFiles() in C:\Users\Derek.Morin\Documents\Visual Studio 2010\Projects\ScriptCode\ScriptCode.ConvertedToC#\Core\CodeDOMCompiler.cs:line 93
   at NewForm.InitializeSystem() in C:\Users\Derek.Morin\Documents\Visual Studio 2010\Projects\ScriptCode\ScriptCode.ConvertedToC#\NewForm.cs:line 179
   at NewForm.NewForm_Load(Object sender, EventArgs e) in C:\Users\Derek.Morin\Documents\Visual Studio 2010\Projects\ScriptCode\ScriptCode.ConvertedToC#\NewForm.cs:line 111
   at System.Windows.Forms.Form.OnLoad(EventArgs e)

我尝试过使用 Thomas Levesque 的建议:

CodeDomProvider objCodeCompiler = new Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider();

然后我得到:

************** Exception Text **************
System.IO.DirectoryNotFoundException: Could not find a part of the path 'C:\Users\Derek.Morin\Documents\Visual Studio 2010\Projects\ScriptCode\ScriptCode.ConvertedToC#\bin\x86\Debug\bin\roslyn\csc.exe'.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
   at Microsoft.CodeDom.Providers.DotNetCompilerPlatform.Compiler.get_CompilerName()
   at Microsoft.CodeDom.Providers.DotNetCompilerPlatform.Compiler.FromFileBatch(CompilerParameters options, String[] fileNames)
   at Microsoft.CodeDom.Providers.DotNetCompilerPlatform.Compiler.CompileAssemblyFromFileBatch(CompilerParameters options, String[] fileNames)
   at System.CodeDom.Compiler.CodeDomProvider.CompileAssemblyFromFile(CompilerParameters options, String[] fileNames)
   at Dynamic.CodeDOMCompiler.CompileAllCodeFiles() in C:\Users\Derek.Morin\Documents\Visual Studio 2010\Projects\ScriptCode\ScriptCode.ConvertedToC#\Core\CodeDOMCompiler.cs:line 87
   at NewForm.InitializeSystem() in C:\Users\Derek.Morin\Documents\Visual Studio 2010\Projects\ScriptCode\ScriptCode.ConvertedToC#\NewForm.cs:line 179
   at NewForm.NewForm_Load(Object sender, EventArgs e) in C:\Users\Derek.Morin\Documents\Visual Studio 2010\Projects\ScriptCode\ScriptCode.ConvertedToC#\NewForm.cs:line 111
   at System.Windows.Forms.Form.OnLoad(EventArgs e)

我不确定它为什么要在我的 bin 目录的子文件夹中查找“csc.exe”。

此路径存在:

C:\Users\Derek.Morin\Documents\Visual Studio 2010\Projects\ScriptCode\ScriptCode.ConvertedToC#\bin\x86\Debug\roslyn

但它正在寻找:

C:\Users\Derek.Morin\Documents\Visual Studio 2010\Projects\ScriptCode\ScriptCode.ConvertedToC#\bin\x86\Debug\bin\roslyn\csc.exe

【问题讨论】:

  • 您的项目的目标是哪个 .NET Framework 版本?
  • 我用详细信息“.NET Framework 4.6”更新了我的问题。我应该指出,相同的代码在 Visual Studio 中编译得很好,但是当我尝试使用 CodeDom 编译它时,就会出现问题。
  • 此功能取决于 C# 语言版本,而不是 .NET Framework 版本。
  • 不知道为什么这个问题被否决了。这是一个合理的问题,答案并不明显。
  • 通过将“roslyn”文件夹复制到它预期的位置 - 我能够让它工作。不过,这似乎是一个黑客行为。不知道是文件复制到哪里的bug,还是寻找编译器的哪里的bug。

标签: c# .net roslyn string-interpolation codedom


【解决方案1】:

更新:2018 年 3 月

请注意,NuGet 版本 1.0.6 ... 1.0.8 将 不要将 /roslyn 文件夹复制到非 Web 上的构建输出目录 项目。最好坚持 1.0.5 https://github.com/aspnet/RoslynCodeDomProvider/issues/38

如@thomas-levesque 所述,使用 C#6 功能的运行时编译需要新的编译器。这个编译器可以使用nuget包Microsoft.CodeDom.Providers.DotNetCompilerPlatform安装。

对于桌面应用程序,存在问题。 ASP.NET 团队以其无限的智慧将编译器的路径硬编码为&lt;runtime-directory&gt;\bin\roslyn\csc.exe 请参阅https://github.com/dotnet/roslyn/issues/9483 的讨论

如果您的桌面应用程序编译为\myapp\app.exe,roslyn 编译器将位于\myapp\roslyn\csc.exeCSharpCodeProvidercsc.exe 解析为\myapp\bin\roslyn\csc.exe

据我所知,你有两个选择

  1. 创建一个后期构建和/或安装例程,将\roslyn 子目录移动到\bin\roslyn
  2. 通过反射黑魔法修复运行时代码。

这是 #2,通过将 CSharpCodeProvider 公开为实用程序类中的属性。

using System.Reflection;
using Microsoft.CodeDom.Providers.DotNetCompilerPlatform;

static Lazy<CSharpCodeProvider> CodeProvider { get; } = new Lazy<CSharpCodeProvider>(() => {
    var csc = new CSharpCodeProvider();
    var settings = csc
        .GetType()
        .GetField("_compilerSettings", BindingFlags.Instance | BindingFlags.NonPublic)
        .GetValue(csc);

    var path = settings
        .GetType()
        .GetField("_compilerFullPath", BindingFlags.Instance | BindingFlags.NonPublic);

    path.SetValue(settings, ((string)path.GetValue(settings)).Replace(@"bin\roslyn\", @"roslyn\"));

    return csc;
});

【讨论】:

  • 非常感谢!这解决了我的问题:)
  • 使用此 hack 时,请记住使用 1.0.5 版或之前的 NuGet 包 Microsoft.CodeDom.Providers.DotNetCompilerPlatform。
  • 为什么,他们修复了什么?
  • @Mustafa 为什么我们应该使用 1.0.5 及更低版本?
  • 编译时csc.exe文件的位置。他们在 1.0.5 之后改变了这一点。
【解决方案2】:

内置的 CodeDOM 提供程序不支持 C# 6。请改用这个:

https://www.nuget.org/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform/

它基于 Roslyn 并支持 C# 6 功能。

只需更改这一行:

CodeDomProvider objCodeCompiler = CodeDomProvider.CreateProvider( "CSharp" );

到这里:

CodeDomProvider objCodeCompiler = new Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider();

【讨论】:

  • “内置 CodeDOM 提供程序不支持 C# 6”:您能否提供该信息的来源以供进一步考虑?对我来说似乎很奇怪CSharpCodeProvider 不使用带有 6 的新编译器 /langversion
  • 通过将“roslyn”文件夹复制到它预期的位置 - 我能够让它工作。不过,这似乎是一个黑客行为。不知道是文件复制到哪里的bug,还是寻找编译器的哪里的bug。
  • @LeonidVasilyev,我最近看到了这个,但我不记得在哪里......对不起。
  • @Derek,为什么要“复制 roslyn 文件夹”?不能直接通过 NuGet 添加包吗?
  • 我确实通过 NuGet 添加了包。但是在运行时,它似乎在错误的位置寻找 csc.exe。如果您看到上面提到的两条路径,您可以看到差异。所以我只是将整个 rosyln 文件夹复制到它似乎正在寻找的位置(作为一种解决方法)。
【解决方案3】:

面临编译器完全损坏的相同问题,除了Aaron's answer中列出的解决方案之外,还找到了第三种解决方案,通过查看库的反编译源我发现,在设置硬编码路径{ProgramLocation}\bin\roslyn之前搜索该位置的环境变量(也是硬编码的),如果设置了,它将使用它。

考虑到这一点,像这样的一些代码也可以“解决”问题:

//Set hardcoded environment variable to set the path to the library
Environment.SetEnvironmentVariable("ROSLYN_COMPILER_LOCATION", "actual compiler location goes here", EnvironmentVariableTarget.Process);
//Create compiler object
CSharpCodeProvider compiler = new CSharpCodeProvider();
//Clean up
Environment.SetEnvironmentVariable("ROSLYN_COMPILER_LOCATION", null, EnvironmentVariableTarget.Process);

//Use "compiler" variable to actually compile the dynamic code

虽然这不会诉诸反射来弄乱内部结构,但它仍然依赖于实现细节,并且像这样滥用环境变量只是感觉不对。与反射替代方案相比,我个人更喜欢这种方法,但同时我知道两者都依赖于确切的实现(以及硬编码路径)。

由于这个问题,以及需要调用外部程序来完成应该在进程中完成的事情,我仍然认为这个库完全被破坏了。

【讨论】:

  • 非常感谢。像魅力一样工作。这是唯一对我有用的。之前都试过了。
【解决方案4】:

最近遇到了这个问题。对于上下文,我尝试使用System.CodeDom 对库项目运行MSTest 项目,但它总是提供一个实现C# 5 的编译器,无论我是否有被测试项目引用的Microsoft.Net.CompilersMicrosoft.CodeDom.Providers.DotNetCompilerPlatform 包。

我的解决方法是:

  • 使用包Microsoft.CodeDom.Providers.DotNetCompilerPlatform
  • 将包PrivateAssets设置为contentfiles;analyzers
  • 通过 provider options 并将 CompilerDirectoryPath 设置为复制的目录

PrivateAssetsdefault valuecontentfiles;analyzers;build,因此要让引用项目也复制文件夹需要从设置中删除 build

示例代码:

var compiler = CodeDomProvider.CreateProvider("cs", new Dictionary<string, string> {
    { "CompilerDirectoryPath", Path.Combine(Environment.CurrentDirectory, "roslyn") }
});

让它与Microsoft.Net.Compilers 一起工作会稍微乏味一些,因为没有复制,但将CompilerDirectoryPath 指向包的工具文件夹的最后步骤是相同的​​。

【讨论】:

  • 在撰写本文时,这是此页面上唯一对我有用的解决方案。简单且不需要假设
【解决方案5】:

更新信息:即使在发布 FW 4.8 之后,您仍然无法使用 C# 8.0 的所有新功能 - 发行版包含 CSC,仅限于 5.0 版;但是有 hack 使用 CSC,随 VS2019 分发(是的,你必须安装它):

var csprovider = new CSharpCodeProvider(new Dictionary<string,string> {
    ["CompilerDirectoryPath"] = @"c:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\Roslyn",
});
options += " -langversion:8.0 ";

var par = new CompilerParameters { GenerateInMemory = true, CompilerOptions = options };
par.ReferencedAssemblies.Add("Microsoft.CSharp.dll");
par.ReferencedAssemblies.Add("System.Core.dll");

var res = csprovider.CompileAssemblyFromSource(par, "your C# code");

return res.CompiledAssembly;// <- compiled result

顺便说一句,尽管有明确的选项“GenerateInMemory”,但您的代码无论如何都会被写入文件,然后才会被编译。请记住,如果您希望您的应用程序在没有磁盘访问的情况下运行。

【讨论】:

  • 点赞。不错的@Vincent,这给了我需要的线索。请注意,Roslyn 的路径取决于您的 VS 版本... @"C:\Program Files (x86)\Microsoft Visual Studio\2019**Enterprise**\MSBuild\Current\Bin\Roslyn";
  • 你为我节省了很多时间,谢谢老兄,我以为我永远也过不下去了 :)
【解决方案6】:

从 Microsoft.CodeDom.Providers.DotNetCompilerPlatform v.3.6.0.0 开始,以下工作:

string RoslynPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\roslyn\\csc.exe";
CSharpCodeProvider Provider = new CSharpCodeProvider(new ProviderOptions(RoslynPath, 0));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-07
    • 1970-01-01
    • 1970-01-01
    • 2012-12-31
    • 1970-01-01
    • 1970-01-01
    • 2020-07-06
    • 2023-03-17
    相关资源
    最近更新 更多