在Roslyn Scripting API中,可以将值作为" globals"的属性传递给脚本。宾语。
使用工作区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;
}
文档正在填充用户脚本,但脚本需要能够从主机应用程序访问某些值。
作为最后的手段,我可以在用户的脚本之前添加变量声明,但这看起来有点乱,我想尽可能避免使用它。
答案 0 :(得分:1)
授予脚本访问全局变量的权限。
创建ProjectInfo时,将HostObjectType
设置为全局类的类型。您可能还需要将MetadataReference添加到定义主机对象类型的程序集中。现在,HostObjectType的成员将对脚本可见。
使用全局变量调用脚本。
对于脚本提交,编译器将所有顶级方法的类合成为其成员,并且顶级语句在逻辑上构成构造函数的主体(但实际上并不是因为异步)。
每个提交还会生成此类型的方法,该方法构造提交类并运行脚本的主体。此方法将object []作为参数。假设此数组的第一个元素是全局对象实例。其他元素保留在提交类实例上,以防有多个提交(当用作像csi.exe这样的REPL时)。
加载生成的程序集(通过Compilation.Emit创建)后,可以通过反射调用此方法。
编辑:您可以在CSharpCompilationOptions中设置生成的脚本类的名称。
答案 1 :(得分:0)
添加到Matt的答案,这是我执行脚本的代码片段:
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>
,它是一个静态方法。第一个参数是我的全局对象。由于我之前没有提交,我将传入null作为第二个参数。