Using C# 6 features with CodeDomProvider (Roslyn)
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;
});
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();
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.