dynamic是C#4中引入的一种特殊类型,它可以让C#在编译时不检查对象调用了哪些操作。这表示可以绕过编译器的类型检查,并在运行时解析这些操作。

dynamic类似于object类型,甚至在用法上部分相同。例如下面的代码

object o = 99;
 
 dynamic d = 99;
 
 Console.WriteLine($"o的type:{o.GetType()}");
 
 Console.WriteLine($"d的type:{d.GetType()}");

输出结果

o的type:System.Int32
d的type:System.Int32

从上面的输出结果可见,变量o和变量d在内存中的真实类型是System.Int32。但是,dynamic所声明的d变量是一个动态类型,编译器不会对变量d的任何操作进行类型安全检查,比如我们可以对变量d执行以下操作,而不会引发语法错误。

d = d + "千克"; // 变成字符串
 
 Console.WriteLine($"d={d} , type={d.GetType()}");            
 
  
 
 d = true;  // 变成bool类型
 
 Console.WriteLine($"d={d} , type={d.GetType()}");
 
  
 
 d = 99f; // 变成float型
 
 Console.WriteLine($"d={d} , type={d.GetType()}");

输出结果

d=99千克 , type=System.String
d=True , type=System.Boolean
d=99 , type=System.Single

结果分析

最初d是System.Int32,后来直接在它后面增加了一个字符串"千克",这样d就变成了System.String类型,然后再赋值为true,d又变成了System.Boolean,最后赋值99f变成float型。

为什么dynamic声明的变量可以做到这一点?

这是因为当我们声明一个动态类型时,编译器会生成一些特殊的代码来处理运行时绑定。也就是说我们在使用动态类型实例时,编译器不会生成IL中间代码来调用或访问它的成员,而是生成一些代码来查找并执行它,这一过程由动态语言运行时(DLR)提供支持。

什么是动态语言运行时(Dynamic Language Runtime)?

动态语言运行时 (DLR) 是一种运行时环境,可以将一组动态语言服务添加到公共语言运行时 (CLR) 。 使用 DLR 可以轻松开发在 .NET Framework 上运行的动态语言,并为静态类型语言添加动态特征。所以,DLR运行于CLR之上,总结一句话,C#的dynamic动态类型在编译时不检查对象的类型,而是在运行时才通过反射等技术去识别这个对象的类型,并执行这个对象的成员

使用DLR可以提高操作的灵活性,但也有一些缺点。首先,动态操作比静态操作要慢得多,因为它们需要额外的步骤来解析和执行。其次,动态操作可能会引发运行时异常,如果它们找不到合适的成员或参数不匹配。最后,动态操作会损失一些编译器提供的功能,如智能感知、重构和代码分析等。

最后,我们来看看object类型是否也可以像dynamic一样操作呢

o = o + "千克"; // 变成字符串
 
 Console.WriteLine($"o={o} , type={o.GetType()}");
 
  
 
 o = true;  // 变成bool类型
 
 Console.WriteLine($"o={o} , type={o.GetType()}");
 
  
 
 o = 99f; // 变成float型
 
 Console.WriteLine($"o={o} , type={o.GetType()}");

输出结果

o=99千克 , type=System.String
o=True , type=System.Boolean
o=99 , type=System.Single

从输出结果上看,object和dynamic似乎是一样的。但是他们在本质上还是有一些区别的,比如变量d在编译时是不确定类型类型的,而变量o在编译时就已经确定了数据类型;在一些复合表达式中,dynamic因为不检查类型,所以不会报错,编译器将有关该操作信息打包在一起,之后这些信息会用于在运行时评估操作,而object类型在参与表达式计算时,则有可能不能通过编译器的语法检查。

——重庆教主 2024年1月21日