dynamic是编译时跳过类型检查、运行时解析成员的静态类型,适用于com互操作、动态语言交互、高度不确定结构的json解析等场景,但会引发runtimebinderexception且丧失ide智能提示。

dynamic 类型不是万能的运行时补丁,它绕过编译期检查,但不会帮你躲开逻辑错误或类型不匹配引发的 RuntimeBinderException。
什么时候该用 dynamic,而不是 object 或泛型?
当你明确需要在运行时才确定成员(方法、属性)是否存在,且这些成员无法通过接口或抽象统一描述时,dynamic 才是合理选择。典型场景包括:调用 COM 对象、与 Python/JavaScript 互操作(如通过 IronPython 或 ClearScript)、解析高度动态的 JSON(不用 JsonElement 或 JObject 时)、写简易 DSL 工具。
别为了“省几行类型声明”而用 dynamic——它会让 IDE 失去跳转、重命名、参数提示能力,也掩盖了本可在编译期发现的拼写错误。
-
object+ 反射也能做类似事,但代码冗长;dynamic是语法糖,底层仍走 DLR(Dynamic Language Runtime)绑定 - 泛型(如
T)在编译期已知结构,dynamic完全放弃结构约束 - 若数据结构固定(哪怕嵌套深),优先用
record、class或JsonSerializer.Deserialize<mydto></mydto>
dynamic 调用失败时,错误发生在哪一行?
错误不是在 dynamic 变量声明时抛出,而是在**实际访问不存在的成员那一行**。例如:
dynamic obj = new ExpandoObject(); obj.Name = "Alice"; Console.WriteLine(obj.Age); // ← 这里才抛出 RuntimeBinderException
这个异常信息通常是:Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'object' does not contain a definition for 'Age'。注意它说 object,不是你原始类型名——DLR 绑定时已擦除原始类型信息。
- 调试时不能靠断点停在“声明行”,得在调用行设断点
- IDE 不会标红,也不会给智能提示,所以建议配合单元测试覆盖关键路径
- 若从外部系统(如 HTTP API)接收数据,先用
JsonSerializer.Deserialize<jsondocument></jsondocument>验证结构,再转dynamic
和 ExpandoObject 搭配用要注意什么?
ExpandoObject 是 dynamic 最常配合的类型,但它本身不是 dynamic——必须显式转成 dynamic 才能享受动态调用语法:
var expando = new ExpandoObject(); dynamic d = expando; // 必须这一步 d.Name = "Bob"; // OK d.SayHello = (Func<string>)(() => "Hi"); // 也可以赋委托 d.SayHello(); // OK</string>
漏掉 dynamic d = expando 这步,直接对 expando 赋值或调用,编译器会报错:“无法对 ExpandoObject 使用动态成员访问”。
-
ExpandoObject实现了IDictionary<string object></string>,所以也可用字典方式操作:((IDictionary<string object>)expando)["Name"] = "Bob"</string> - 多个
dynamic变量之间运算(如d1 + d2)会触发 DLR 运行时重载解析,性能比静态类型低一个数量级,高频路径慎用 - 序列化
ExpandoObject时,默认 JSON 序列化器(System.Text.Json)能正确处理,但Newtonsoft.Json需要确保没开启TypeNameHandling,否则可能写入 $type 字段
真正难的不是怎么写 dynamic,而是判断“这里到底需不需要它”。多数所谓“灵活”的需求,其实用配置驱动+策略模式+小范围反射就能更安全地解决。一旦用了 dynamic,就得为缺失的编译期保障多写一层运行时校验或测试覆盖。











