Использование функций C# 6 С CodeDomProvider (rosyln)

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 Основы 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:UsersDerek.MorinDocumentsVisual Studio 2010ProjectsScriptCodeScriptCode.ConvertedToC#CoreCodeDOMCompiler.cs:line 93
   at NewForm.InitializeSystem() in C:UsersDerek.MorinDocumentsVisual Studio 2010ProjectsScriptCodeScriptCode.ConvertedToC#NewForm.cs:line 179
   at NewForm.NewForm_Load(Object sender, EventArgs e) in C:UsersDerek.MorinDocumentsVisual Studio 2010ProjectsScriptCodeScriptCode.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:UsersDerek.MorinDocumentsVisual Studio 2010ProjectsScriptCodeScriptCode.ConvertedToC#binx86Debugbinroslyncsc.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:UsersDerek.MorinDocumentsVisual Studio 2010ProjectsScriptCodeScriptCode.ConvertedToC#CoreCodeDOMCompiler.cs:line 87
   at NewForm.InitializeSystem() in C:UsersDerek.MorinDocumentsVisual Studio 2010ProjectsScriptCodeScriptCode.ConvertedToC#NewForm.cs:line 179
   at NewForm.NewForm_Load(Object sender, EventArgs e) in C:UsersDerek.MorinDocumentsVisual Studio 2010ProjectsScriptCodeScriptCode.ConvertedToC#NewForm.cs:line 111
   at System.Windows.Forms.Form.OnLoad(EventArgs e)

Я не уверен, почему он пытается искать "КБК.exe" в папку Мои папку bin.

этот путь существует:

C:UsersDerek.MorinДокументыVisual Studio 2010ПроектыScriptCodeScriptCode.ConvertedToC#binx86Debugroslyn

но он искал:

C:UsersDerek.MorinДокументыVisual Studio 2010ПроектыScriptCodeScriptCode.ConvertedToC#binx86Debugbinroslyncsc.exe

4 ответов


встроенный поставщик 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();

Обновление: Март 2018

слово предостережения, NuGet версии 1.0.6 ... 1.0.8 будет не копировать папку / roslyn в выходной каталог сборки на не-web проекты. Лучше придерживаться 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 as \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;
});

столкнулся с той же проблемой полностью сломанного компилятора и нашел третье решение в дополнение к перечисленным в Аарон, посмотрев на декомпилированный источник библиотеки, я нашел это, прежде чем установить жестко закодированный путь {ProgramLocation}\bin\roslyn он ищет переменную среды (также жестко закодированную) для этого местоположения, и если она установлена, она использует ее вместо этого.

имея это в виду, некоторый код, подобный этому, также "исправит" проблему:

//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

пока это не прибегает к рефлексии, чтобы возиться с внутренними, он по-прежнему полагается на детали реализации и злоупотребление переменными среды, как это просто кажется неправильным. Мне лично это нравится больше, чем альтернатива отражения, но в то же время я знаю, что оба опираются на точную реализацию (а также на жестко закодированный путь).

из-за этой проблемы и необходимости вызова внешней программы для выполнения того, что должно быть сделано в процессе, я все еще считаю эту библиотеку полностью сломана.


недавно столкнулся с этой проблемой. Для контекста я пытался запустить проект MSTest против проекта библиотеки, используя System.CodeDom, но он всегда давал компилятор, который реализовал C# 5 независимо от того, имел ли я Microsoft.Net.Compilers или Microsoft.CodeDom.Providers.DotNetCompilerPlatform пакеты, на которые ссылается тестируемого проекта.

мое исправление для этого было:

  • использовать пакет Microsoft.CodeDom.Providers.DotNetCompilerPlatform
  • комплект поставки PrivateAssets to contentfiles;analyzers
  • передать параметры поставщика С CompilerDirectoryPath набор в скопированный каталог

на значение по умолчанию на PrivateAssets is contentfiles;analyzers;build, поэтому получение ссылок на проекты, чтобы также скопировать папку, требует удаления build из настройки.

пример кода:

var compiler = CodeDomProvider.CreateProvider("cs", new Dictionary<string, string> {
    { "CompilerDirectoryPath", Path.Combine(Environment.CurrentDirectory, "roslyn") }
});

заставить это работать с Microsoft.Net.Compilers было бы немного более утомительно, поскольку копия не сделана, но конечный шаг указания CompilerDirectoryPath в папке инструментов пакета то же самое.