Подтвердить что ты не робот

Использование функций С# 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

4b9b3361

Ответ 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

Насколько я могу судить, у вас есть два варианта

  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;
});

Ответ 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', ваш код в любом случае будет записан в файл и только после этого будет скомпилирован. Имейте в виду, если вы хотите, чтобы ваше приложение запускалось без доступа к диску.