发布于2026-07-18 阅读(0)
扫一扫,手机访问
聊到 C# 的 dynamic,很多人第一反应是“运行时类型”,但这种理解其实不太对。它并不是运行时态,而更像是一张编译器给开的“后门通行证”。简单说,你把变量声明为 dynamic,编译器就不再检查你有没有写对成员名、参数个数对不对、能不能赋值——这些事全推到运行时,交给 DLR(动态语言运行时)去处理。要是用错了地方,直接甩你一个 RuntimeBinderException,连个调试的余地都不给。
dynamic 不可?关键不在于“能不能装任意值”,而在于“调用成员的时候要不要走 DLR 绑定”。你可以试试看,object 上调用方法直接编译不过去(除非你用反射),而 dynamic 会把整个调用链——方法名、参数、重载选择——打包成一个 CallSite,等到运行时才去查目标对象到底有没有那个方法。哪些场景非它不可?
Marshal.Invoke 或者 Type.InvokeMembermshtml 或 Microsoft.Office.Interop.Excel),属性名和方法名是字符串驱动的Newtonsoft.Json.Linq.JObject 转 dynamic 后直接点属性dynamic 赋值后类型变了吗?这个要分清楚:变量本身类型始终是 dynamic,但所指向的底层值可以是任意类型。IntelliSense 显示的是声明类型,不是值类型。看个例子就明白了:
dynamic d = 42;
Console.WriteLine(d.GetType()); // System.Int32
d = "hello";
Console.WriteLine(d.GetType()); // System.String
d = new[] { 1, 2, 3 };
Console.WriteLine(d.GetType()); // System.Int32[]
这里 var 真代替不了。var d = 42; 推导出来是 int,后面不能再赋值字符串。但 dynamic 可以跨类型赋值,而且每次 .GetType() 返回的都是真实运行时类型。
dynamic_ec.exampleMethod1(10, 4) 是运行时抛异常而不是编译报错?编译器对 dynamic 表达式的任何成员访问——方法、属性、索引器——都生成 CallSite,把调用信息缓存起来,等到第一次执行才尝试绑定。DLR 的查找顺序是这样的:
exampleMethod1,并且参数个数、类型能隐式转换匹配IDynamicMetaObjectProvider,走自定义绑定逻辑Microsoft.CSharp.RuntimeBinder.RuntimeBinderException所以即使 ExampleClass 只定义了 exampleMethod1(int i),dynamic_ec.exampleMethod1(10, 4) 还是会走到第 4 步失败,错误信息类似 'ExampleClass' does not contain a definition for 'exampleMethod1'。
dynamic 绑定的开销比想象中大。首次调用慢——要构建和缓存 CallSite;后续同签名调用快——命中缓存;但一旦参数类型组合发生变化(比如从 int 换成 long),又触发新绑定。更麻烦的是调试体验:
dynamic 行时,Locals 窗口只显示 dynamic,看不到实际值结构(得鼠标悬停或用 QuickWatch 输入 d.ToString())Microsoft.CSharp.RuntimeBinder 内部dynamic 传入泛型方法时,类型推导会失效:DoSomething(dynamicValue) 里 T 会被推为 dynamic,而不是实际运行时类型真正难缠的不是语法本身,而是你得想清楚:这个 dynamic 值从哪来、生命周期内可能是什么类型、谁负责保证它有你要调的方法。这些责任全移交给了程序员,编译器不再兜底。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8