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

Der*_*rek 31 .net c# codedom string-interpolation roslyn

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

CompilerParameters objCompilerParameters = new CompilerParameters();

...

CompilerResults objCompileResults = objCodeCompiler.CompileAssemblyFromFile( objCompilerParameters, files.ToArray() );
Run Code Online (Sandbox Code Playgroud)

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

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

有没有人知道如何使用CodeDom编译进行字符串插值?

我找到了这个链接:如何使用CSharpCodeProvider定位.net 4.5?

所以我尝试过:

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

     // Instantiate the compiler.
     CodeDomProvider objCodeCompiler = CodeDomProvider.CreateProvider( "CSharp", providerOptions );
Run Code Online (Sandbox Code Playgroud)

但我仍然得到同样的错误.

我还将目标框架更新到.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)
Run Code Online (Sandbox Code Playgroud)

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

CodeDomProvider objCodeCompiler = new Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider();
Run Code Online (Sandbox Code Playgroud)

但后来我得到:

************** 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)
Run Code Online (Sandbox Code Playgroud)

我不确定为什么它试图在我的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

Aar*_*don 27

更新:2018年3月

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

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

对于桌面应用程序,存在问题.ASP.NET团队以其无限的智慧对编译器的路径进行了硬编码,<runtime-directory>\bin\roslyn\csc.exe请参阅https://github.com/dotnet/roslyn/issues/9483

如果你的桌面应用程序被编译成\myapp\app.exe,在罗斯林编译器将位于\myapp\roslyn\csc.exe,BUT THE CSharpCodeProvider将解决csc.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;
});
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢你!这解决了我的问题:) (2认同)

Tho*_*que 22

内置的CodeDOM提供程序不支持C#6.请使用此代码:

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

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

只需改变这一行:

CodeDomProvider objCodeCompiler = CodeDomProvider.CreateProvider( "CSharp" );
Run Code Online (Sandbox Code Playgroud)

对此:

CodeDomProvider objCodeCompiler = new Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider();
Run Code Online (Sandbox Code Playgroud)

  • "内置的CodeDOM提供程序不支持C#6":您是否可以提供该信息的来源以供进一步考虑?对我来说似乎很奇怪`CSharpCodeProvider`不使用带有6的新编译器[/langversion ](https://msdn.microsoft.com/en-us/library/6ds95cz0(v=vs.140).aspx) (3认同)
  • 我确实通过NuGet添加了包.但是在运行时似乎在错误的位置寻找csc.exe.如果您看到上面提到的两条路径,您可以看到差异.所以我只是将整个rosyln文件夹复制到它看起来正在寻找的位置(作为一种解决方法). (3认同)
  • 通过将“roslyn”文件夹复制到预期的位置 - 我能够使其正常工作。不过,这看起来像是一个黑客。我不知道这是文件复制到的位置的错误,还是寻找编译器的位置的错误。 (2认同)

Ale*_*dro 6

面对完全损坏的编译器的相同问题,除了亚伦的答案中列出的解决方案之外,还找到了第三种解决方案,通过查看库的反编译源,我发现,在设置硬编码路径之前,{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
Run Code Online (Sandbox Code Playgroud)

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

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


tyc*_*hon 5

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

我对此的解决方法是:

  • 使用包 Microsoft.CodeDom.Providers.DotNetCompilerPlatform
  • 将包设置PrivateAssetscontentfiles;analyzers
  • 通过供应商的选择CompilerDirectoryPath设置为复制的目录中

默认值PrivateAssetscontentfiles;analyzers;build,因此让参照项目也复制文件夹需要删除build从设置。

示例代码:

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

使用它Microsoft.Net.Compilers会稍微有点乏味,因为没有复制,但指向CompilerDirectoryPath包的工具文件夹的最后一步是相同的。


Vin*_*ent 5

更新信息:即使在发布 FW 4.8 之后,您仍然无法使用 C# 8.0 的所有新功能 - 发行版包含 CSC,仅限于 5.0 版;但是使用 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
Run Code Online (Sandbox Code Playgroud)

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