roslyn scripting api 是 .net 提供的轻量级脚本执行能力,适合配置驱动逻辑、规则引擎、调试辅助和动态表达式求值等场景;需显式引用程序集、传入 globals 对象、注意类型可见性与异步限制。

什么是 Roslyn Scripting API,它适合做什么
Roslyn Scripting API 是 .NET 提供的轻量级脚本执行能力,不是编译整个项目,而是运行一段字符串形式的 C# 代码。它不替代完整编译流程,但适合配置驱动逻辑、规则引擎、调试辅助、自动化任务中的动态表达式求值等场景。
- 不能直接引用未加载的程序集(比如
Newtonsoft.json),必须显式添加引用 - 不支持
async方法体直接作为脚本主体(需包装为同步调用或使用ScriptOptions启用异步) - 脚本默认运行在沙箱中,无文件系统/网络访问权限,需手动注入依赖对象
如何安装和基础运行一个 C# 脚本
从 .NET 5 开始,microsoft.CodeAnalysis.Scripting 已内置在 SDK 中,无需额外 NuGet 包;若用 .NET Core 3.1 或更早版本,需安装 Microsoft.CodeAnalysis.Scripting NuGet 包(注意匹配 SDK 版本)。
using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; <p>var result = await CSharpScript.EvaluateAsync<int>("1 + 2 * 3"); console.WriteLine(result); // 输出 7</p>
-
EvaluateAsync返回Task<t></t>,必须await或.Result(后者可能死锁) - 类型参数
<int></int>必须与脚本最后一行表达式的实际类型一致,否则抛CompilationErrorException - 字符串内容不能含顶层类/命名空间声明,只允许表达式、语句块或单个方法体
如何传入变量和自定义类型到脚本中
脚本默认无法访问外部作用域变量,必须通过 globals 对象传入。该对象需是具体类实例,其公开字段/属性才能被脚本读写。
public class ScriptGlobals { public int Count { get; set; } = 10; public string Prefix { get; set; } = "test"; } <p>var globals = new ScriptGlobals(); var result = await CSharpScript.EvaluateAsync<string>( "Prefix + Count.ToString()", ScriptOptions.default.WithReferences(typeof(Console).Assembly), globals);</p>
-
WithReferences必须显式加入所有脚本中用到的类型所在程序集(如typeof(Console).Assembly) - 若脚本中调用自定义类方法,该类必须是
public,且方法为public实例方法或静态方法 - 不支持传入
dynamic或匿名类型作 globals,编译会失败
常见错误和绕过限制的关键点
CS1685:找不到类型 —— 多数因漏掉 WithReferences 或引用了未导出的内部类型CS0103:名称不存在 —— 变量未在 globals 中定义,或拼写与属性名不一致OperationCanceledException:脚本执行超时,默认 30 秒,可设 ScriptOptions.Default.WithTimeout(TimeSpan.FromSeconds(60))
- 脚本中不能用
using Static,但可用using(需在脚本开头写) - 若需多次执行相似脚本,复用
ScriptOptions实例能提升性能(避免重复解析引用) - 编译错误信息藏在
CompilationErrorException的Diagnostics属性里,建议捕获后打印以便调试
Roslyn Scripting 看似简单,但引用管理、类型可见性、异步上下文和超时控制这几处,稍不注意就会卡住几小时。尤其在容器或函数计算环境里,缺少调试输出时,Diagnostics 几乎是唯一线索。