




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

var 是编译期推导,类型一旦确定就不可变(var x = "a"; x = 123; 直接编译报错); object 要访问成员必须显式转换(((string)obj).Length),IDE 能提示、能重构; dynamic 所有成员访问、方法调用、运算符都跳过编译检查,全靠运行时 DLR 解析,写错 obj.Lengh(拼错)也能过编译,直到执行才抛 RuntimeBinderException。它不是语法糖,而是为特定互操作瓶颈设计的“减压阀”。
Excel.Application,不用写一长串 Marshal.ReleaseComObject 和 InvokeMember; Newtonsoft.Json.Linq.JObject 或 System.Text.Json.JsonNode 反序列化后,直接 data.users[0].name 访问; MethodInfo 调用逻辑,换成 dynamic obj = target; obj.DoSomething(); 更直白; IDynamicMetaObjectProvider 实现自定义绑定。❌ 别用在业务模型层、DTO 传输、循环内高频访问——性能损耗明显(DLR 缓存虽有,但首次解析开销大),且 IDE 完全失能。
动态不等于随意,几个实操习惯能大幅降低风险:
obj is string 或 obj?.GetType() == typeof(int) 做类型守门; dynamic; ExpandoObject 构建可写动态对象时,注意 ExpandoObject 本身实现了 IDictionary,可遍历属性名,方便做字段校验; obj.GetType() 或 ((IDynamicMetaObjectProvider)obj).GetMetaObject(Expression.Constant(obj)) 查看当前绑定状态。DLR 的首次调用比静态调用慢 5–10 倍(主要花在元数据查找和缓存构建上),后续同签名调用会命中缓存,差距缩小到 1.2–1.5 倍。
for 循环里反复调用 item.Name(其中 item 是 dynamic)就会明显拖慢; Convert.ToString(item.Name) 或提前转成具体类型(string name = item.Name;),让后续访问回归静态路径。真正容易被忽略的是调试成本——断点停住后,你没法靠鼠标悬停看属性,得手动敲 obj.GetType()、obj.ToString(),甚至进“快速监视”查 IDynamicMetaObjectProvider 实现细节。这不是语法问题,是开发流被打断。