dynamic是编译器层面的静态类型标记,跳过静态检查,将成员访问等全部推迟至运行时由dlr处理,错误抛runtimebinderexception;必须用dynamic而非object的场景包括com互操作、动态语言桥接、dom/json泛化解析等。

dynamic 不是“运行时类型”,它是编译器层面的静态类型标记,作用是跳过所有静态检查,把成员访问、方法调用、运算符解析全部推迟到运行时由 DLR 处理;用错地方不会报编译错误,而是直接在运行时抛 RuntimeBinderException。
什么时候必须用 dynamic 而不是 object
关键不在“能不能装值”,而在“调用成员时是否走 DLR 绑定”:
-
object上写.Method()会直接编译失败(除非反射) -
dynamic上写.Method()编译通过,但运行时查不到就崩 - COM 互操作(如
Microsoft.Office.Interop.Excel)不加dynamic就得手写一长串Marshal.Invoke或Type.InvokeMember - 接收 IronPython / Ruby.NET 返回值,方法名和参数结构在 C# 编译期完全不可知
- 解析
JObject或HtmlElement这类属性名动态生成的对象,比如json.name、dom.innerHTML
dynamic 赋值后类型不会变,但 .GetType() 返回真实值类型
dynamic 变量的声明类型永远是 dynamic,但它所指向的底层值可以随时切换类型:
dynamic d = 42;
Console.WriteLine(d.GetType()); // System.Int32
<p>d = "hello";
Console.WriteLine(d.GetType()); // System.String</p><p>d = new[] { 1, 2, 3 };
Console.WriteLine(d.GetType()); // System.Int32[]</p>
这跟 var 完全不同:var d = 42 推导出的是 int,后续不能再赋字符串;而 dynamic 允许跨类型赋值,且每次 .GetType() 都反映当前实际值类型。
为什么 dynamic 调用方法报 RuntimeBinderException 而不是编译错误
因为编译器对 dynamic 表达式不做任何绑定,只打包成 CallSite<t></t>,等第一次执行时才由 DLR 查找:
- 先查目标对象是否有公开实例方法,参数个数、类型能否隐式转换匹配
- 再查是否有适用的扩展方法(要求命名空间已
using) - 最后查是否实现了
IDynamicMetaObjectProvider,走自定义绑定逻辑 - 三者全失败 → 抛
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
这个过程无法提前预判,所以调试时容易卡在运行时才暴露问题——尤其当对象来自 JSON 解析或 COM,结构松散、文档缺失时,错误位置往往离调用点很远。
最容易被忽略的一点:所有涉及 dynamic 的表达式(比如 a + b、a[i]、(string)a)都会触发 DLR 绑定,哪怕看起来只是类型转换;一旦运行时值类型不支持该操作,就崩,而且没有栈帧提示具体哪一步出的问题。











