Использование функций С# 6 с CodeDomProvider (Roslyn)
CodeDomProvider objCodeCompiler = CodeDomProvider.CreateProvider( "CSharp" );
CompilerParameters objCompilerParameters = new CompilerParameters();
...
CompilerResults objCompileResults = objCodeCompiler.CompileAssemblyFromFile( objCompilerParameters, files.ToArray() );
Когда я скомпилирую свои файлы, я получаю:
FileFunctions.cs(347): Ошибка: Неожиданный символ '$'
Кто-нибудь знает, как получить строчную интерполяцию, работающую с компиляцией CodeDom?
Я нашел эту ссылку: Как настроить таргетинг на .net 4.5 с помощью 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)
Я не уверен, почему он пытается найти "csc.exe" в подпапке моего каталога bin.
Этот путь существует:
C:\Users\Derek.Morin\Documents\Visual Studio 2010\Projects\ScriptCode\ScriptCode.ConvertedToС#\Bin\x86\Debug\Рослин
Но он искал:
C:\Users\Derek.Morin\Documents\Visual Studio 2010\Projects\ScriptCode\ScriptCode.ConvertedToС#\Bin\x86\Debug\Bin\Рослин\csc.exe
Ответы
Ответ 1
Встроенный поставщик CodeDOM не поддерживает С# 6. Вместо этого используйте это:
https://www.nuget.org/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform/
Он основан на Roslyn и поддерживает функции С# 6.
Просто измените эту строку:
CodeDomProvider objCodeCompiler = CodeDomProvider.CreateProvider( "CSharp" );
:
CodeDomProvider objCodeCompiler = new Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider();
Ответ 2
Обновление: март 2018
Предупреждение: NuGet версии 1.0.6... 1.0.8 не будет копировать папку /roslyn в выходной каталог сборки в не-веб-проектах. Лучшая флешка с 1.0.5 https://github.com/aspnet/RoslynCodeDomProvider/issues/38
Компиляция во время выполнения с использованием возможностей С# 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
Насколько я могу судить, у вас есть два варианта
- Создайте подпрограмму после сборки и/или установки, которая переместит подкаталог
\roslyn
в \bin\roslyn
. - Исправьте код выполнения через отражение черной магии.
Вот # 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;
});
Ответ 3
Недавно столкнулся с этим вопросом. Для контекста я пытался запустить проект MSTest для библиотечного проекта с использованием System.CodeDom
, но он всегда давал компилятор, который реализовывал С# 5, независимо от того, были ли на меня ссылки на пакеты Microsoft.Net.Compilers
или Microsoft.CodeDom.Providers.DotNetCompilerPlatform
по тестируемому проекту.
Мое исправление для этого было:
- Используйте пакет
Microsoft.CodeDom.Providers.DotNetCompilerPlatform
- Установить пакет
PrivateAssets
для contentfiles;analyzers
- Передайте параметры провайдера с установленным
CompilerDirectoryPath
в скопированный каталог
Значением по умолчанию для PrivateAssets
является contentfiles;analyzers;build
, поэтому для получения ссылок на проекты, которые также копируют папку, необходимо удалить build
из параметра.
Пример кода:
var compiler = CodeDomProvider.CreateProvider("cs", new Dictionary<string, string> {
{ "CompilerDirectoryPath", Path.Combine(Environment.CurrentDirectory, "roslyn") }
});
Приступить к работе с Microsoft.Net.Compilers
было бы немного более утомительным, так как не было сделано никаких копий, но последний шаг указания CompilerDirectoryPath
на папку инструментов пакета такой же.
Ответ 4
Столкнулся с той же проблемой полностью сломанного компилятора и нашел третье решение в дополнение к тем, которые перечислены в ответе Аарона, просмотрев декомпилированный источник библиотеки, я обнаружил, что перед установкой жестко закодированного пути {ProgramLocation}\bin\roslyn
it ищет переменную окружения (также жестко запрограммированную) для этого местоположения, и если установлено, она использует ее вместо этого.
Имея это в виду, некоторый код, подобный этому, также "исправит" проблему:
//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
Хотя это не прибегает к размышлениям, чтобы связываться с внутренними компонентами, оно все еще опирается на детали реализации и злоупотребление переменными среды, как это просто кажется неправильным. Лично мне это нравится больше, чем альтернатива отражения, но в то же время я знаю, что оба зависят от точной реализации (а также от жестко заданного пути).
Из-за этой проблемы и необходимости вызова внешней программы для выполнения того, что должно быть сделано в процессе, я все еще считаю эту библиотеку полностью сломанной.
Ответ 5
Обновленная информация: даже после выпуска FW 4.8 вы все еще не можете использовать все новые функции С# 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
Кстати, несмотря на явную опцию 'GenerateInMemory', ваш код в любом случае будет записан в файл и только после этого будет скомпилирован. Имейте в виду, если вы хотите, чтобы ваше приложение запускалось без доступа к диску.