web-dev-qa-db-ja.com

CodeDomProvider(Roslyn)でのC#6機能の使用

CodeDomProvider objCodeCompiler = CodeDomProvider.CreateProvider( "CSharp" );

CompilerParameters objCompilerParameters = new CompilerParameters();

...

CompilerResults objCompileResults = objCodeCompiler.CompileAssemblyFromFile( objCompilerParameters, files.ToArray() );

ファイルをコンパイルすると、次のようになります。

FileFunctions.cs(347):エラー:予期しない文字 '$'

誰かがCodeDomコンパイルで文字列補間を機能させる方法を知っていますか?

私はこのリンクを見つけました: CSharpCodeProviderで.net 4.5をターゲットにする方法?

だから私は試しました:

     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)

Thomas Levesqueの提案を使用してみました。

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)

Binディレクトリのサブフォルダーで「csc.exe」を検索しようとしている理由がわかりません。

このパスは存在します:

C:\ Users\Derek.Morin\Documents\Visual Studio 2010\Projects\ScriptCode\ScriptCode.ConvertedToC#\ bin\x86\Debug\roslyn

しかし、それは探していました:

C:\ Users\Derek.Morin\Documents\Visual Studio 2010\Projects\ScriptCode\ScriptCode.ConvertedToC#\ bin\x86\Debug\bin\roslyn\csc.exe

34
Derek

組み込みの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();
24
Thomas Levesque

更新:2018年3月

注意:NuGetバージョン1.0.6 ... 1.0.8は、非Webプロジェクトのビルド出力ディレクトリに/ roslynフォルダーをコピーしません。 1.0.5のベストスティック https://github.com/aspnet/RoslynCodeDomProvider/issues/38

@ thomas-levesqueが述べたように、C#6機能を使用したランタイムコンパイルには新しいコンパイラが必要です。このコンパイラは、nugetパッケージ Microsoft.CodeDom.Providers.DotNetCompilerPlatform を使用してインストールできます。

デスクトップアプリケーションの場合、問題があります。 ASP.NETチームは、その無限の知恵により、コンパイラへのパスを<runtime-directory>\bin\roslyn\csc.exeとしてハードコーディングしました https://github.com/dotnet/roslyn/issues/948 のディスカッションを参照してください

デスクトップアプリケーションが\myapp\app.exeにコンパイルされている場合、roslynコンパイラは\myapp\roslyn\csc.exeにありますが、CSharpCodeProvidercsc.exeを解決します\myapp\bin\roslyn\csc.exeとして

私の知る限り、2つの選択肢があります

  1. \roslynサブディレクトリを\bin\roslynに移動するビルド後および/またはインストールルーチンを作成します。
  2. リフレクションブラックマジックを介してランタイムコードを修正します。

これは、ユーティリティクラスのプロパティとしてCSharpCodeProviderを公開することによる#2です。

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;
});
30
Aaron Hudon

最近この問題に遭遇しました。コンテキストとして、System.CodeDomを使用してライブラリプロジェクトに対してMSTestプロジェクトを実行しようとしましたが、Microsoft.Net.CompilersまたはMicrosoft.CodeDom.Providers.DotNetCompilerPlatformパッケージが参照されているかどうかに関係なく、C#5を実装するコンパイラが常に提供されましたテスト中のプロジェクトによって。

これに対する私の修正は:

  • パッケージMicrosoft.CodeDom.Providers.DotNetCompilerPlatformを使用
  • パッケージPrivateAssetscontentfiles;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で機能させると、コピーが作成されないので少し面倒になりますが、パッケージのtoolsフォルダーでCompilerDirectoryPathをポイントする最後の手順は同じです。

4
tychon

更新された情報:FW 4.8をリリースした後でも、C#8.0のすべての新機能を使用できません-ディストリビューションにはバージョン5.0に限定されたCSCが含まれています。しかし、VS2019で配布されているCSCを使用するハックがあります(そう、インストールする必要があります)。

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」にもかかわらず、コードはとにかくファイルに書き込まれ、それからコンパイルされます。アプリケーションをディスクアクセスなしで実行する場合は注意してください。

3
Vincent

完全に壊れたコンパイラの同じ問題に直面し、ハードコードされたパス{ProgramLocation}\bin\roslynを設定する前に、私が見つけたライブラリの逆コンパイルされたソースを見て、 Aaronの答え にリストされているものに加えて3番目の解決策を見つけましたその場所の環境変数(これもハードコーディングされている)を検索し、設定されている場合は代わりにそれを使用します。

このことを念頭に置いて、次のようなコードも問題を「修正」します。

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

これは内部の混乱にリフレクションを使用するわけではありませんが、実装の詳細に依存しており、このような環境変数の悪用は間違っていると感じています。私は個人的にリフレクションの選択肢よりもこれを気に入っていますが、同時に両方とも正確な実装(およびハードコードされたパス)に依存していることも知っています。

この問題と、インプロセスで実行する必要があることを実行するために外部プログラムを呼び出す必要があるため、このライブラリは完全に壊れていると考えています。

1
Alejandro