在CodeDomProvider(Roslyn)中使用C#6功能

问题描述 投票:29回答:5
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)

我尝试使用托马斯·列维斯克的建议:

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 Studio2010 \ Projects \ ScriptCode \ ScriptCode.ConvertedToC#\ bin \ x86 \ Debug \ roslyn

但是它正在寻找:

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

c# roslyn .net-standard string-interpolation codedom
5个回答
20
投票

内置的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();

27
投票

更新:2018年3月

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

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

对于桌面应用程序,有问题。 ASP.NET团队以其无穷的智慧将编译器的路径硬编码为Microsoft.CodeDom.Providers.DotNetCompilerPlatform,请参见<runtime-directory>\bin\roslyn\csc.exe的讨论>

如果您的桌面应用程序已编译为https://github.com/dotnet/roslyn/issues/9483,则ros​​lyn编译器将位于\myapp\app.exe但是\myapp\roslyn\csc.exe将解析为CSharpCodeProvidercsc.exe

据我所知,您有两个选择

  1. 创建将将\myapp\bin\roslyn\csc.exe子目录移动到\roslyn的构建后和/或安装例程。
  2. 通过反射黑魔法修复运行时代码。
  3. 这里是#2,通过将\bin\roslyn公开为实用程序类中的属性。

    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; }); 对一个库项目运行一个MSTest项目,但是无论我是否有被测试项目引用的System.CodeDomMicrosoft.Net.Compilers包,它始终为实现C#5的编译器提供了帮助。

我对此的解决方法是:

  • 使用软件包Microsoft.CodeDom.Providers.DotNetCompilerPlatform
  • 将程序包Microsoft.CodeDom.Providers.DotNetCompilerPlatform设置为PrivateAssets
  • contentfiles;analyzers设置为复制的目录传递provider options
  • CompilerDirectoryPathdefault valuePrivateAssets,因此要获取引用项目以也复制文件夹,需要从设置中删除contentfiles;analyzers;build

    示例代码:

build

将其与var compiler = CodeDomProvider.CreateProvider("cs", new Dictionary<string, string> { { "CompilerDirectoryPath", Path.Combine(Environment.CurrentDirectory, "roslyn") } }); 一起使用将比较繁琐,因为没有进行复制,但是将Microsoft.Net.Compilers指向软件包的tools文件夹的结束步骤是相同的​​。

面对完全破碎的编译器的同一问题,除了CompilerDirectoryPath中列出的解决方案之外,还找到了第三个解决方案,通过查看库的反编译源,我发现,在设置硬编码路径Aaron's answer之前,它进行了搜索用于该位置的环境变量(也经过硬编码),并且如果设置了它,则使用该变量。

考虑到这一点,类似这样的一些代码也会“解决”问题:

{ProgramLocation}\bin\roslyn

虽然这并不求助于反射来弄乱内部,但它仍然依赖于实现细节,并且滥用环境变量就像这样,感觉很不对劲。我个人更喜欢这种方式,而不是反射方式,但同时我知道两者都依赖于确切的实现(以及硬编码的路径)。

由于此问题,以及需要调用外部程序来执行应在进程内完成的操作,因此我仍然认为该库已完全损坏。

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

//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

BTW尽管有显式选项'GenerateInMemory',但无论如何,您的代码都会写入文件,然后才进行编译。请记住,如果您希望您的应用程序不通过磁盘访问即可运行。


3
投票

最近遇到了这个问题。就上下文而言,我试图使用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; }); 对一个库项目运行一个MSTest项目,但是无论我是否有被测试项目引用的System.CodeDomMicrosoft.Net.Compilers包,它始终为实现C#5的编译器提供了帮助。


1
投票

面对完全破碎的编译器的同一问题,除了CompilerDirectoryPath中列出的解决方案之外,还找到了第三个解决方案,通过查看库的反编译源,我发现,在设置硬编码路径Aaron's answer之前,它进行了搜索用于该位置的环境变量(也经过硬编码),并且如果设置了它,则使用该变量。


1
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.