将 C# 6 功能与 CodeDomProvider (Roslyn) 结合使用
Using C# 6 features with CodeDomProvider (Roslyn)
CodeDomProvider objCodeCompiler = CodeDomProvider.CreateProvider( "CSharp" );
CompilerParameters objCompilerParameters = new CompilerParameters();
...
CompilerResults objCompileResults = objCodeCompiler.CompileAssemblyFromFile( objCompilerParameters, files.ToArray() );
当我编译我的文件时,我得到:
FileFunctions.cs(347): Error: Unexpected character '$'
有谁知道如何使用 CodeDom 编译进行字符串插值?
我找到这个 link: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
内置的 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();
Update: March 2018
Word of caution, NuGet version 1.0.6 ... 1.0.8 will
not copy the /roslyn folder to the build output directory on non-web
projects. Best stick with 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
,roslyn 编译器将位于 \myapp\roslyn\csc.exe
、,但 CSharpCodeProvider
将解析 csc.exe
作为 \myapp\bin\roslyn\csc.exe
据我所知,你有两个选择
- 创建 post-build and/or 安装例程,将
\roslyn
子目录移动到 \bin\roslyn
.
- 通过反射黑魔法修复运行时代码。
这是#2,通过在实用程序 class.
中将 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;
});
遇到完全损坏的编译器的相同问题,除了 中列出的解决方案外,还找到了第三个解决方案,通过查看库的反编译源,我发现在设置硬编码路径之前{ProgramLocation}\bin\roslyn
它会为该位置搜索一个环境变量(也是硬编码的),如果已设置,它会改用该变量。
考虑到这一点,像这样的一些代码也会 "fix" 问题:
//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
虽然这不会诉诸反射来扰乱内部结构,但它仍然依赖于实现细节,并且像这样滥用环境变量感觉不对。我个人更喜欢这个而不是反射替代方案,但同时我知道两者都依赖于确切的实现(以及硬编码路径)。
因为这个问题,而且需要调用外部程序来做该做的事情in-process,我还是认为这个库彻底坏掉了。
运行 最近关注这个问题。对于上下文,我试图 运行 一个 MSTest 项目针对一个使用 System.CodeDom
的库项目,但它总是提供一个实现 C# 5 的编译器,无论我是否有 Microsoft.Net.Compilers
或 Microsoft.CodeDom.Providers.DotNetCompilerPlatform
被测项目引用的包。
我对此的修复是:
- 使用包
Microsoft.CodeDom.Providers.DotNetCompilerPlatform
- 将包
PrivateAssets
设置为 contentfiles;analyzers
- 传递 provider options 并将
CompilerDirectoryPath
设置为复制的目录
PrivateAssets
的 default value 是 contentfiles;analyzers;build
,因此要使引用项目也复制文件夹需要从设置中删除 build
。
示例代码:
var compiler = CodeDomProvider.CreateProvider("cs", new Dictionary<string, string> {
{ "CompilerDirectoryPath", Path.Combine(Environment.CurrentDirectory, "roslyn") }
});
让它与 Microsoft.Net.Compilers
一起工作会稍微乏味一些,因为没有复制,但是指向 CompilerDirectoryPath
包的工具文件夹的结束步骤是相同的。
更新信息:即使在发布 FW 4.8 之后,您仍然无法使用 C# 8.0 的所有新功能 - 发行版包含 CSC,仅限于 5.0 版;但是有使用 CSC 的 hack,与 VS2019 一起分发(是的,你必须安装它):
var csprovider = new CSharpCodeProvider(new Dictionary<string,string> {
["CompilerDirectoryPath"] = @"c:\Program Files (x86)\Microsoft Visual Studio19\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',您的代码无论如何都会被写入文件,然后才会被编译。请记住,如果您希望您的应用程序 运行 w/o 磁盘访问。
自 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));
CodeDomProvider objCodeCompiler = CodeDomProvider.CreateProvider( "CSharp" );
CompilerParameters objCompilerParameters = new CompilerParameters();
...
CompilerResults objCompileResults = objCodeCompiler.CompileAssemblyFromFile( objCompilerParameters, files.ToArray() );
当我编译我的文件时,我得到:
FileFunctions.cs(347): Error: Unexpected character '$'
有谁知道如何使用 CodeDom 编译进行字符串插值?
我找到这个 link: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
内置的 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();
Update: March 2018
Word of caution, NuGet version 1.0.6 ... 1.0.8 will not copy the /roslyn folder to the build output directory on non-web projects. Best stick with 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
,roslyn 编译器将位于 \myapp\roslyn\csc.exe
、,但 CSharpCodeProvider
将解析 csc.exe
作为 \myapp\bin\roslyn\csc.exe
据我所知,你有两个选择
- 创建 post-build and/or 安装例程,将
\roslyn
子目录移动到\bin\roslyn
. - 通过反射黑魔法修复运行时代码。
这是#2,通过在实用程序 class.
中将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;
});
遇到完全损坏的编译器的相同问题,除了 {ProgramLocation}\bin\roslyn
它会为该位置搜索一个环境变量(也是硬编码的),如果已设置,它会改用该变量。
考虑到这一点,像这样的一些代码也会 "fix" 问题:
//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
虽然这不会诉诸反射来扰乱内部结构,但它仍然依赖于实现细节,并且像这样滥用环境变量感觉不对。我个人更喜欢这个而不是反射替代方案,但同时我知道两者都依赖于确切的实现(以及硬编码路径)。
因为这个问题,而且需要调用外部程序来做该做的事情in-process,我还是认为这个库彻底坏掉了。
运行 最近关注这个问题。对于上下文,我试图 运行 一个 MSTest 项目针对一个使用 System.CodeDom
的库项目,但它总是提供一个实现 C# 5 的编译器,无论我是否有 Microsoft.Net.Compilers
或 Microsoft.CodeDom.Providers.DotNetCompilerPlatform
被测项目引用的包。
我对此的修复是:
- 使用包
Microsoft.CodeDom.Providers.DotNetCompilerPlatform
- 将包
PrivateAssets
设置为contentfiles;analyzers
- 传递 provider options 并将
CompilerDirectoryPath
设置为复制的目录
PrivateAssets
的 default value 是 contentfiles;analyzers;build
,因此要使引用项目也复制文件夹需要从设置中删除 build
。
示例代码:
var compiler = CodeDomProvider.CreateProvider("cs", new Dictionary<string, string> {
{ "CompilerDirectoryPath", Path.Combine(Environment.CurrentDirectory, "roslyn") }
});
让它与 Microsoft.Net.Compilers
一起工作会稍微乏味一些,因为没有复制,但是指向 CompilerDirectoryPath
包的工具文件夹的结束步骤是相同的。
更新信息:即使在发布 FW 4.8 之后,您仍然无法使用 C# 8.0 的所有新功能 - 发行版包含 CSC,仅限于 5.0 版;但是有使用 CSC 的 hack,与 VS2019 一起分发(是的,你必须安装它):
var csprovider = new CSharpCodeProvider(new Dictionary<string,string> {
["CompilerDirectoryPath"] = @"c:\Program Files (x86)\Microsoft Visual Studio19\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',您的代码无论如何都会被写入文件,然后才会被编译。请记住,如果您希望您的应用程序 运行 w/o 磁盘访问。
自 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));