本文共 8177 字,大约阅读时间需要 27 分钟。
引言
经过前面专题的介绍,大家应该对C# 1和C# 2中的特性有了进一步的理解了吧,现在终于迎来我们期待已久的C# 3中特性,C# 中Lambda表达式和Linq的提出相当于彻底改变我们之前的编码风格了,刚开始接触它们,一些初学者肯定会觉得很难理解,但是我相信,只要多多研究下并且弄明白之后你肯定会爱上C# 3中的所有特性的,因为我自己就是这么过来的,在去年的这个时候,我看到Lambda表达式和Linq的时候觉得很难理解,而且觉得很奇怪的(因为之前都是用C# 3之前的特性去写代码的,虽然C# 3中的特性已经出来很久了,但是自己却写的很少,也没有怎么去研究,所以就觉得很奇怪,有一种感觉就是——怎么还可以这样写的吗?),经过这段时间对C# 语言系统的学习之后,才发现新的特性都是建立在以前特性的基础上的,只是现在编译器去帮助我们解析C# 3中提出的特性,所以对于编译器而言,用C# 3.0中的特性编写的代码和C# 2.0中编写的代码是一样的。从这个专题开始,将会为大家介绍C# 3 中的特性,本专题就介绍下C# 3中提出来的一些基础特性,这些特性也是Lambda表达式和Linq的基础。
一、自动实现的属性
当我们在类中定义的属性不需要一些额外的验证时,此时我们可以使用自动实现的属性使属性的定义更加简洁,对于C# 3中自动实现的属性,编译器编译时会创建一个私有的匿名的字段,该字段只能通过属性的get和set访问器进行访问。下面就看一个C#3中自动实现的属性的例子:
有些人会问——你怎么知道编译器会帮我们生成一个匿名的私有字段的呢?对于这点当然通过反射工具来查看经过编译器编译之后的代码了,下面是用Reflector工具查看的一张截图:
如果在结构体中使用自动属性时,则所有构造函数都需要显式地调用无参构造函数this(),否则,就会出现编译时错误,因为只有显式调用无参构造函数this(),编译器才知道所有字段都被赋值了。下面是一段测试代码:
把this()注释掉后就会出现编译时错误,如下图:
二、隐式类型
用关键字var定义的变量则该变量就是为隐式类型,var 关键字告诉编译器根据变量的值类推断变量的类型。所以对于编译器而言,隐式类型同样也是显式的,同样具有一个显式的类型。
2.1 隐式类型的局部变量
用var 关键字来声明局部变量,下面一段演示代码:
为什么说用var定义的变量对于编译器来说还是具有显式类型呢?在Visual studio中,将鼠标放在var部分的时候就可以看到编译器为变量推断的类型。并且变量仍然是静态类型,只是我们在代码中没有写出类型的名称而已,这个工作交给编译器根据变量的值去推断出变量的类型,为了证明变量时静态类型,当我们把2赋给变量stringvariable时就会出现编译时错误,然而在其他动态语言中,这样的赋值是可以编译通过,所以用var声明的变量仍然还是静态类型,只是我们在代码中没有写出来而已。下面是证明上面两点的截图:
然而使用隐式类型时有一些限制,具体限制有:
// 不能用一个未赋值的变量来初始化隐私类型 // 如果变量s被初始化了就可以了 string s; var stringvariable = s;
)
同时使用隐式类型有优点也有缺点,下面的一段示例代码完全诠释了:
2.2 隐式类型的数组
var不仅可以创建隐式类型的局部变量,还可以创建数组,下面是一段演示代码:
使用隐式类型的数组时,编译器必须推断出使用什么类型的数组,编译器首先会构造一个包含大括号里面的所有表达式(如上面代码中的 1,2,3,4和"hello","learning hard")的编译时类型的集合,在这个集合中如果所有类型都能隐式转换为卫衣的一种类型,则该类型就成为数组的类型,否则,就会出现编译时错误,如代码中隐式类型数组出错的情况, 因为"hello"转化为string,而3却转化为int,此时编译器就不能确定数组的类型到底为什么,所以就会出现编译错误,错误信息为:"找不到隐式类型数组的最佳类型"
三、对象集合初始化
3.1 对象初始化
有了对象初始化特性之后,我们就不需要考虑定义参数不同的构造函数来应付不同情况的初始化了,就减少了在我们实体类中定义的构造函数代码,这样使代码更加简洁,下面就具体看下C# 3中的对象初始化的使用和注意事项:
上面代码中我用红色标注出使用对象初始化时需要注意的地方,大家也可以通过反射工具查看编译器是如何去解析对象初始化代码的。
3.2 集合初始化
C# 3中还提出了集合初始化特性来对集合初始化进行了优化,下面是一段集合初始化的使用演示代码:
集合初始化同样是编译器自动帮我们调用List的无参构造函数,然后调用Add()方法一个一个地添加进去,对于编译器而言,C# 3中使用集合初始化的代码和C#3之前写的代码是一样.然而对于开发人员来说,有了C#3的集合初始化之后,这个过程就不需要我们自己去编码,而是交给编译器帮我们做就好了, 为了证明编译器帮我们所做得事情,下面看看用反射工具来查看编译器到底是怎样帮我们来翻译集合初始化的:
从上面反射出来的代码可以看出,编译器确实是一位大好人,帮我们做了那么多的事情。
可能大家会有这样的疑问——对象集合初始化只不过是一个语法糖而已,就是简单地让我们少写点代码而已啊,也没有其他什么用啊?下面部分的介绍将会解决你们的疑问。四、匿名类型
看到匿名类型可能大家会联想到前面介绍的匿名方法,编译器对匿名类型和匿名方法都采用同样的处理方式,该方式为编译器为匿名类型生成类型名,我们在代码中不需要显式自定义一个类型,下面就看看匿名类型的使用:
运行结果:
上面匿名类型的演示中使用了前面几部分介绍的所有特性——隐式类型,对象集合初始化,所以对于前面说对象集合初始化也没有其他方面的用处的疑问也可以得到答案了,如果没有对象集合初始化,要写出这样的代码(指的是 var person1 = new { Name = "learning hard", Age = 25 };)还可能吗?所以前面的隐式类型和对象集合初始化另外的一个用处就是服务于匿名类型的, 然而匿名类型又是服务于Linq的,对于Linq的好处当时是多的数不胜数了, 后面专题中会为大家介绍Linq。
上面还指出虽然我们在代码中没有为匿名类型指定类型名,而编译器会为我们生成一个类型,为了证明这点我们同样反射工具Reflector查看下编译器最后为我们生成的代码到底是怎样的?截图如下:
从上面截图中可以看出编译器确实为我们生成了一个匿名类型)<<Name>j__TPar, <Age>j__TPar>(其中代码相当于我们上面中定义的Person类),编译器为我们生成的这个类型是直接继承自System.Object的,并且是internal sealed(指的是该类型只在程序集内可见,并且不能被继承)。
五、总结
到这里,本专题的介绍也就结束了, 本专题就介绍了C# 3中几个基础的特性——自动实现的属性、隐式类型、对象集合初始化和匿名类型,这些类型的提出都是服务于后面更复杂的特性Linq的,所以只有掌握好这些基础特性之后,才能更好更快地掌握好Linq。在后面一个专题将和大家聊下C#3中的Lambda表达式。
转载地址:http://estbl.baihongyu.com/