1、概念
先来补充下理论知识,Dynamic类型是C#4.0中引入的新类型,它允许其操作掠过编译器类型检查,而在运行时处理。
编程语言有时可以划分为静态类型化语言和动态类型化语言。C#和Java经常被认为是静态化类型的语言,而Python、Ruby和JavaScript是动态类型语言。一般而言,动态语言在编译时不会对类型进行检查,而是在运行时识别对象的类型,动态语言有利有弊:代码编写起来更快、更容易,但无法获取编译器错误,只能通过单元测试和其他方法来确保应用正常运行。C#最初是作为纯静态语言创建的,但是C#4添加了一些动态元素,用于改进与动态语言和框架之间的互操作性,C# 团队考虑了多种设计选项,但最终确定添加一个新关键字来支持这些功能:Dynamic。Dynamic关键字可充当C#类型系统中的动态类型声明。这样,C#就获得了动态功能。由于编译时不会去检查类型,所以导致IDE的IntellSense(智能提示)失效。
2、示例
using System; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { dynamic dyn = 1; dyn += 1; Console.WriteLine($"类型:{dyn.GetType()};值:{dyn}"); Console.ReadKey(); } } }
运行结果如下:
3、注意事项
-
由于dynamic类型的变量在设计时是未知的,因此无法看到Visual Studio对dynamic类型变量的成员进行自动提示,因为编译器也不知道。
-
由于dynamic类型变量的具体类型要在运行时才能确定,因此你对该变量的成员调用必须是正确的,如果调用了变量没有的属性或方法,将会产生异常。
using System; namespace ConsoleApp1 { public class Program { static void Main(string[] args) { Person p = new Person(); p.Name = "张三"; ShowInfo(p); Console.WriteLine(p.Name); Console.ReadKey(); } static void ShowInfo(dynamic d) { Console.WriteLine(d.Name); d.Name = "神灵武士"; } } public class Person { public string Name { get; set; } } }
二、匿名类型
隐式类型的局部变量是支持匿名类型机制而加入C#的。在linq中使用的较多。var的使用又优点也有缺点,这里这说下优缺点,毕竟不同的公司技术要求不一样。
1、优点
-
写代码更加灵活,可以将更少的注意力放到返回值的类型上
-
当使用linq查询的时候尽量使用var,下面举例说明:IEnumerable<T>是IQueryable<T>的父类,比如有些查询所获得的的结果是IQueryable<T>,但是我们使用IEnumerable<T>接收返回值也不会报错,但是我们就不能使用IQueryable<T>提供的方法了:
public IEnumerable<string> Find(string start) { IEnumerable<string> q = from c in db.Customers select c.ContactName; var q2 = q.Where(s => s.StartsWith(start)); return q2; }
针对上面的代码,我们详细说明下:第一行的代码会将数据库中客户名称全部查询出来,因为涉及到查库,返回的类型是IQueryable<T>,但是我们使用了IEnumerable<T>来接收,因为IEnumerable<T>是IQueryable<T>的父类,所以不会出现编译错误,但是会带来性能问题。第二条语句使用的不是IQueryable.where,而是IEnumerable.where。IQueryable查询能够把与查询有关的多个表达式树组合成一项,即将上边的第一句和第二句代码构建出一个完整的树,一次性执行完毕,通常会在数据存放的远程服务器上执行;IEnumerable在远程服务器上执行第一句代码,然后将数据拿到本地,再进行第二句代码的执行。所以上边的代码会影响性能。但是如果改成下面代码,系统会自动匹配最合适的返回类型:
public IEnumerable<string> Find(string start) { var q = from c in db.Customers select c.ContactName; var q2 = q.Where(s => s.StartsWith(start)); return q2; }
2、缺点
-
易读性降低
-
编译时需要进行类型转换
- 对于值类型的返回值,使用var会出现精确度的错误。所以如果是值类型,不要使用var。
三、Dynamic、Var和Object比较
先说说var,经常有人会拿dynamic和var进行比较。实际上,var和dynamic完全是两个概念,根本不应该放在一起做比较。var实际上编译器抛给我们的语法糖,一旦被编译,编译器就会自动匹配var变量的实际类型,并用实际类型来替换该变量的声明,等同于我们在编码时使用了实际类型声明。而dynamic被编译后是一个Object类型,编译器编译时不会对dynamic进行类型检查。
再说说Object,上面提到dynamic类型再编译后是一个Object类型,同样是Object类型,那么两者的区别是什么呢?除了在编译时是否进行类型检查之外,另外一个重要的区别就是类型转化,这也是dynamic很有价值的地方,dynamic类型的实例和其他类型的实例间的转换是很简单的,开发人员能够很方便地在dyanmic和非dynamic行为间切换。任何实例都能隐式转换为dynamic类型实例,见下面的例子:
dynamic d1 = 7; dynamic d2 = "a string"; dynamic d3 = System.DateTime.Today; dynamic d4 = System.Diagnostics.Process.GetProcesses();
反之亦然,类型为dynamic的任何表达式也能够隐式转换为其他类型。
int i = d1; string str = d2; DateTime dt = d3; System.Diagnostics.Process[] procs = d4;
但是object需要涉及到装箱和拆箱,这里不在赘述该知识点。