2017-10-03 12 views
0

Roslyn Scripting APIでは、値をスクリプトに渡して「グローバル」オブジェクトのプロパティとして渡すことができます。Roslyn:(SourceCodeKind.Scriptを使用して)ドキュメントに変数を渡すことが可能です

ワークスペースAPIを使用するときに同様のことを行うことはできますか?

は、ここに私のサンプルコードです:

var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) 
      .WithOverflowChecks(true).WithOptimizationLevel(OptimizationLevel.Release) 
      .WithUsings("System", "System.Collections", "System.Collections.Generic", "System.Dynamic", "System.Linq"); 

string userCode = "... end user's code goes here..."; 

using (var workspace = new AdhocWorkspace() { }) 
{ 
    string projName = "NewProject132"; 
    var projectId = ProjectId.CreateNewId(); 
    var projectInfo = ProjectInfo.Create(
     projectId, 
     VersionStamp.Create(), 
     projName, 
     projName, 
     LanguageNames.CSharp, 
     isSubmission: true, 
     compilationOptions: options, 
     metadataReferences: references, 
     parseOptions: new CSharpParseOptions(kind: SourceCodeKind.Script, languageVersion: LanguageVersion.Latest)); 
    var project = workspace.AddProject(projectInfo); 

    var id = DocumentId.CreateNewId(project.Id); 


    /* 
     how do I declare variables that are supposed to be visible to the user's code? 
     */ 

    var solution = project.Solution.AddDocument(id, project.Name, userCode); 
    var document = solution.GetDocument(id); 

    //get syntax and semantic errors 
    var syntaxTree = document.GetSyntaxTreeAsync().Result; 
    foreach (var syntaxError in syntaxTree.GetDiagnostics()) 
    { 
     //... 
    } 
    var model = document.GetSemanticModelAsync().Result; 
    foreach (var syntaxError in model.GetDiagnostics(new TextSpan(0, userCode.Length))) 
    { 
     //... 
    } 
    var completionService = CompletionService.GetService(document); 
    var completions = completionService.GetCompletionsAsync(document, userCode.Length - 1).Result; 
} 

文書は、ユーザースクリプトが移入されているが、スクリプトは、ホストアプリケーションからいくつかの値にアクセスできるようにする必要があります。

最後の手段として、変数宣言をユーザーのスクリプトの前に追加することができますが、それはちょっと面倒なようです。可能であれば避けたいと思います。

答えて

1

スクリプトにグローバルへのアクセス権を与える。

ProjectInfoを設定すると、グローバルクラスの種類にHostObjectTypeが設定されます。また、ホストオブジェクトタイプが定義されているアセンブリにMetadataReferenceを追加する必要があります.HostObjectTypeのメンバーがスクリプトに表示されるようになりました。

グローバルを使用してスクリプトを呼び出す。

スクリプトの提出では、コンパイラはすべてのトップレベルメソッドをメンバとしてクラスを合成し、最上位レベルのステートメントは論理的にコンストラクタの本体を構成します(実際は非同期のためではありません)。

各サブミッションは、サブミットクラスを構築し、スクリプトの本体を実行するこのタイプのメソッドも生成します。このメソッドは、オブジェクト[]をパラメータとして取ります。この配列の最初の要素は、グローバルオブジェクトインスタンスと見なされます。他の要素は、複数のサブミッションがある場合(csi.exeのようなREPLとして使用される場合)に、サブミッションクラスインスタンスを保持します。

生成されたアセンブリ(Compilation.Emitで作成)を読み込んだ後、このメソッドをリフレクションで呼び出すことができます。

編集:生成されたスクリプトクラスの名前は、CSharpCompilationOptionsで設定できます。コールする

var compilation = document.GetSemanticModelAsync().Result.Compilation; 
using (MemoryStream ms = new MemoryStream()) 
{ 
    var emitResult = compilation.Emit(ms); 
    var assembly = Assembly.Load(ms.GetBuffer()); 
    Type t = assembly.GetTypes().First(); 

    var res = t.GetMethod("<Factory>").Invoke(null, new object[] { new object[] { Activator.CreateInstance(_customType), null } }); 
} 

メソッドは静的メソッドだ、<Factory>です:マットの答えに追加

+0

優れた答え。 HostObjectTypeパラメータを指定すると、オートコンプリートで使用可能な変数が作成され、シンボルの適切な検出が有効になり、リフレクションスタッフが実行をソートしました。ありがとう! – anakic

0

は、ここにスクリプトを実行するための私のコードスニペットです。最初のパラメータはmy globalsオブジェクトです。私は前回の投稿がないので、2番目のパラメータとしてnullを渡しています。

関連する問題