dynamic是编译器放弃类型检查的明确信号,var为编译期推导且类型固定,object需显式转换;dynamic适用于COM交互、动态JSON、反射封装及脚本桥接,禁用于业务模型与高频循环。

dynamic 和 var、object 到底差在哪?
dynamic 不是“更灵活的 var”,也不是“带智能提示的 object”——它是编译器主动放弃类型检查的明确信号。
-
var是编译期推导,类型一旦确定就不可变(var x = "a"; x = 123;直接编译报错); -
object要访问成员必须显式转换(((string)obj).Length),IDE 能提示、能重构; -
dynamic所有成员访问、方法调用、运算符都跳过编译检查,全靠运行时 DLR 解析,写错obj.Lengh(拼错)也能过编译,直到执行才抛RuntimeBinderException。
什么场景真该用 dynamic?
它不是语法糖,而是为特定互操作瓶颈设计的“减压阀”。
- ✅ 和 COM 对象交互:比如
Excel.Application,不用写一长串Marshal.ReleaseComObject和InvokeMember; - ✅ 处理未知结构 JSON:用
Newtonsoft.Json.Linq.JObject或System.Text.Json.JsonNode反序列化后,直接data.users[0].name访问; - ✅ 包装反射调用:比如你有一堆
MethodInfo调用逻辑,换成dynamic obj = target; obj.DoSomething();更直白; - ✅ 构建脚本桥接层(如暴露 C# 方法给 Lua/Python 调用),配合
IDynamicMetaObjectProvider实现自定义绑定。
❌ 别用在业务模型层、DTO 传输、循环内高频访问——性能损耗明显(DLR 缓存虽有,但首次解析开销大),且 IDE 完全失能。
怎么安全地用 dynamic?避免 runtime 崩溃
动态不等于随意,几个实操习惯能大幅降低风险:
- 永远在调用前用
obj is string或obj?.GetType() == typeof(int)做类型守门; - 对来自外部的数据(如 API JSON),优先用强类型反序列化;仅当 schema 真正多变、且字段数少时,才退到
dynamic; - 配合
ExpandoObject构建可写动态对象时,注意ExpandoObject本身实现了IDictionary,可遍历属性名,方便做字段校验; - 在调试时,直接在监视窗口输入
obj.GetType()或((IDynamicMetaObjectProvider)obj).GetMetaObject(Expression.Constant(obj))查看当前绑定状态。
性能到底慢多少?要不要担心?
DLR 的首次调用比静态调用慢 5–10 倍(主要花在元数据查找和缓存构建上),后续同签名调用会命中缓存,差距缩小到 1.2–1.5 倍。
- 单次调用(如初始化 Excel、解析一个配置项)完全可忽略;
- 但在
for循环里反复调用item.Name(其中item是dynamic)就会明显拖慢; - 替代方案:用
Convert.ToString(item.Name)或提前转成具体类型(string name = item.Name;),让后续访问回归静态路径。
真正容易被忽略的是调试成本——断点停住后,你没法靠鼠标悬停看属性,得手动敲 obj.GetType()、obj.ToString(),甚至进“快速监视”查 IDynamicMetaObjectProvider 实现细节。这不是语法问题,是开发流被打断。










