问题
CodeDomProvider objCodeCompiler = CodeDomProvider.CreateProvider( "CSharp" );
CompilerParameters objCompilerParameters = new CompilerParameters();
...
CompilerResults objCompileResults = objCodeCompiler.CompileAssemblyFromFile( objCompilerParameters, files.ToArray() );
When I compile my files I get:
FileFunctions.cs(347): Error: Unexpected character '$'
Does anyone know how to get string interpolation working with CodeDom compiling?
I found this link: How to target .net 4.5 with CSharpCodeProvider?
So I tried:
var providerOptions = new Dictionary<string, string>();
providerOptions.Add( "CompilerVersion", "v4.0" );
// Instantiate the compiler.
CodeDomProvider objCodeCompiler = CodeDomProvider.CreateProvider( "CSharp", providerOptions );
But I still get the same error.
I also updated the target framework to .NET Framework 4.6.
NOTE: I can't specify "v4.5" or "v4.6" or I will get:
************** 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)
I have tried using the suggestion by Thomas Levesque:
CodeDomProvider objCodeCompiler = new Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider();
But then I get:
************** 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)
I'm not sure why it is trying to look for "csc.exe" in a subfolder of my bin directory.
This path exists:
C:\Users\Derek.Morin\Documents\Visual Studio 2010\Projects\ScriptCode\ScriptCode.ConvertedToC#\bin\x86\Debug\roslyn
But it was looking for:
C:\Users\Derek.Morin\Documents\Visual Studio 2010\Projects\ScriptCode\ScriptCode.ConvertedToC#\bin\x86\Debug\bin\roslyn\csc.exe
回答1:
The built-in CodeDOM provider doesn't support C# 6. Use this one instead:
https://www.nuget.org/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform/
It's based on Roslyn and supports the C# 6 features.
Just change this line:
CodeDomProvider objCodeCompiler = CodeDomProvider.CreateProvider( "CSharp" );
to this:
CodeDomProvider objCodeCompiler = new Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider();
回答2:
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
Run-time compilation using C#6 features requires a new compiler, as @thomas-levesque mentioned. This compiler can be installed by using the nuget package Microsoft.CodeDom.Providers.DotNetCompilerPlatform.
For desktop applications, there's a problem. The ASP.NET team, in their infinite wisdom have hard-coded the path to the compiler as <runtime-directory>\bin\roslyn\csc.exe
See discussion at https://github.com/dotnet/roslyn/issues/9483
If your desktop application is compiled to \myapp\app.exe
, the roslyn compiler will be located at \myapp\roslyn\csc.exe
, BUT THE CSharpCodeProvider
WILL RESOLVE csc.exe
as \myapp\bin\roslyn\csc.exe
As far as I can tell, you have two options
- Create a post-build and/or installation routine that will move the
\roslyn
subdirectory to\bin\roslyn
. - Fix the runtime code through reflection black magic.
Here is #2, by exposing the CSharpCodeProvider
as a property in a utility class.
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;
});
回答3:
Ran into this issue recently. For context, I was trying to run an MSTest project against a library project using System.CodeDom
, but it always gave a compiler that implemented C# 5 whether or not I had Microsoft.Net.Compilers
or Microsoft.CodeDom.Providers.DotNetCompilerPlatform
packages referenced by the project under test.
My fix for this was:
- Use package
Microsoft.CodeDom.Providers.DotNetCompilerPlatform
- Set package
PrivateAssets
tocontentfiles;analyzers
- Pass provider options with
CompilerDirectoryPath
set to the copied directory
The default value for PrivateAssets
is contentfiles;analyzers;build
, so getting referencing projects to also copy the folder requires removing build
from the setting.
Example code:
var compiler = CodeDomProvider.CreateProvider("cs", new Dictionary<string, string> {
{ "CompilerDirectoryPath", Path.Combine(Environment.CurrentDirectory, "roslyn") }
});
Getting this to work with Microsoft.Net.Compilers
would be slightly more tedious as no copy is made, but the end step of pointing CompilerDirectoryPath
at the package's tools folder is the same.
回答4:
Faced the same issue of the completely broken compiler and found a third solution in addition to those listed in the Aaron's answer, by looking at the decompiled source of the library I found that, before setting the hardcoded path {ProgramLocation}\bin\roslyn
it searches for an environment variable (also hardcoded) for that location, and if set, it uses that instead.
With that in mind, some code like this would also "fix" the problem:
//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
While this doesn't resorts to reflection to mess with the internals, it still relies on implementation details and abusing environment variables like this just feels wrong. I personally like this more than the reflection alternative, but at the same time I know that both relies on the exact implementation (as well as the hardcoded path).
Because of this issue, and the need to call an external program to do what should be done in-process, I still consider this library to be completely broken.
回答5:
Updated information: even after releasing FW 4.8 you still not able to use all new features of C# 8.0 - distro contains CSC, limited to version 5.0; But there is hack to use CSC, distributed with VS2019 (yes, you have to install it):
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
BTW despite explicit option 'GenerateInMemory', your code anyway will be written to the file and only then will be compiled. Keep in mind if you want your application run w/o disk access.
来源:https://stackoverflow.com/questions/31639602/using-c-sharp-6-features-with-codedomprovider-roslyn