.NET中那些所谓的新语法之一:自动属性、隐式类型、命名参数与自动初始化器

开篇:在日常的.NET开发学习中,我们往往会接触到一些较新的语法,它们相对以前的老语法相比,做了很多的改进,简化了很多繁杂的代码格式,也大大减少了我们这些菜鸟码农的代码量。但是,在开心欢乐之余,我们也不禁地对编译器内部到底为我们做了哪些事儿而感到好奇?于是,我们就借助反编译神器,去看看编译器到底做了啥事!其实本篇中很多都不算新语法,对于很多人来说可能都是接触了很久了,这里主要是针对.NET的老版本来说,是一个“相对”的新语法。

/* 新语法索引 */

1.自动属性 Auto-Implemented Properties
2.隐式类型 var
3.参数默认值 和 命名参数
4.对象初始化器 与 集合初始化器 { }
5.匿名类 & 匿名方法
6.扩展方法
7.系统内置委托 Func / Action
8.Lambda表达式
9.标准查询运算符 Standard Query Operator
10.LINQ查询表达式

一、自动属性探秘

1.1 以前的做法:先写私有变量,再写公有属性

    public class Student{private Int32 _id;public Int32 Id{get { return _id; }set { _id = value; }}private string _name;public string Name{get { return _name; }set { _name = value; }}private Int16 _age;public Int16 Age{get { return _age; }set { _age = value; }}}

1.2 现在的做法:声明空属性

    public class Person{public Int32 ID { get; set; }public string Name { get; set; }public Int16 Age { get; set; }}

PS:现在看来,是不是少些很多代码?直接声明一个空属性,编译器就可以帮我们完成以前的私有成员字段和get、set方法,于是,我们可以通过Reflector反编译工具去看看,到底是怎么完成这个操作的。

1.3 伟大的“乡村基”—CSC(C Sharp Compiler):C#编译器

PS:这里为何会提到乡村基,一是因为乡村基的简称就是CSC,二是因为本人比较喜欢吃乡村基的中式快餐,所以,么么嗒!(感觉像是给乡村基打广告似的,不过我还是蛮喜欢乡村基的,当然是抛开价格来说)

  (1)首先我们来编译一下上面这个小程序,然后将编译后的exe/dll拖到反编译神器Reflector(或者ILSpy也是赞赞哒)中

  (2)找到Person类,可以看到编译后的结果:CSC帮我们自动生成了与共有属性对应的私有字段

  我们可以从图中看出,自动生成的字段与以前的字段有一些区别:

  ①在每个字段上方都加上了一个[CompilerGenerated]的特性(Attribute),顾名思义:表示其是由编译器生成的;

  ②每个字段的变量名称是有一定格式的,比如<Age>k__BackingField,那么可以看出格式为:<属性名>k_BackingField;(BackingField顾名思义就是背后的字段)

  (3)看完了自动生成的字段,再来看看属性是怎么定义的:

  ①和自动生成的字段一样,属性也加上了[CompilerGenerated]的特性以示区别

  ②众所周知,属性就是一个get和一个set的两个方法的封装,那么我们之前写的空get/set方法又是怎么被编译生成的呢

  于是,我们可以看到,在get和set方法中,也加上了[CompilerGenerated]的特性以示区别,另外还帮我们自动对应了自动生成的私有字段,这就跟我们自己手动写的私有字段+共有属性的方法保持了一致。所以,自动属性是一个实用的语法糖,帮我们做了两件事:自动生成私有字段,自动在get/set方法中匹配私有字段。

二、隐式类型—关键字:var

2.1 犹抱琵琶半遮面—你能猜出我是谁?

  以前,我们在定义每个变量时都需要明确指出它是哪个类型。但是,当有了var之后,一切变得那么和谐,我们可以用一个var定义所有的类型。

    var age = 100;age += 150;var name = "";name = "edisonchou";Console.WriteLine("age={0}", age);Console.WriteLine("name={0}", name);

  点击调试,发现编译器自动帮我们匹配上了正确的类型并成功显示出来:

  那么,我们又好奇地想知道编译器到底是否识别出来了指定的类型,于是我们再次通过反编译工具来一看究竟:

  可以看出,我们可爱的CSC正确地帮我们推断出了正确的类型,不由得想给它点32个赞了!

  但是,变量类型不可更改,因为声明的时候已经确定类型了,例如我们在刚刚的代码中给变量赋予不同于定义时的类型,会出现错误。

2.2 好刀用在刀刃上—隐式类型应用场景

  在数据型业务开发中,我们会对一个数据集合进行LINQ查询,而这个LINQ查询的结果可能是ObjectQuery<>或IQueryable<>类型的对象。因此,在目标具体类型不明确的情况下,我们可以用var关键来声明:

List<UserInfo> userList = roleService.LoadRoles(param);
var data = from u in userListwhere u.IsDel == 0select u;

2.3 但“爱”就是克制—隐式类型使用限制

  (1)被声明的变量是一个局部变量,而不是静态或实例字段;

  (2)变量必须在声明的同时被初始化,编译器要根据初始化值推断类型;

  (3)初始化不是一个匿名函数,同时初始化表达式也不能是 null;

  (4)语句中只声明一次变量,声明后不能更改类型;(详见上面的例子)

  (5)赋值的数据类型必须是可以在编译时确定的类型;

三、参数默认值和命名参数

3.1 带默认值的方法

        static void Main(string[] args){// 01.带默认值参数函数
            FuncWithDefaultPara();// 02.省略一个默认参数调用FuncWithDefaultPara(10086);Console.ReadKey();}static void FuncWithDefaultPara(int id = 10010, bool gender = true){Console.WriteLine("Id:{0},Gender:{1}", id,gender ? "Man" : "Woman");}

  点击调试,显示结果如下:

3.2 编译后的方法调用

  同样,为了一探带参数默认值方法调用的细节,我们还是借助反编译神器查看其中的玄妙:

  (1)首先,我们来看看带默认值参数的方法被编译后是怎么的:

  可以看到,在.NET Framework中大量采用了基于Attribute的开发方式,这里为参数添加了表示默认值的特性DefaultParameterValue。

  (2)其次,再来看看Main函数中的调用过程是怎么被编译的:

  

  可以看出,编译器帮我们在方法调用的括号中帮我们填充了默认值。这里,我们不禁好奇,如果在调用中,不指定ID(即使用ID默认值10010)而仅仅指定Gender为false是否可以编译通过?我们来试一下:

        static void Main(string[] args){// 01.带默认值参数函数
            FuncWithDefaultPara();// 02.省略一个默认参数调用FuncWithDefaultPara(10086);// 错误调用:FuncWithDefaultPara(false);Console.ReadKey();}

  这时,出现了以下错误:

  于是,我们知道,CSC也还没有那么智能,无法理解我们高深的“意图”。那么,有木有一种方法来解决这种需求呢,于是命名参数横空出世了。

3.3 使用命名参数

  在新语法中为方法调用引入了命名参数,格式为 参数名:参数值

        static void Main(string[] args){// 01.带默认值参数函数
            FuncWithDefaultPara();// 02.省略一个默认参数调用FuncWithDefaultPara(10086);// 错误调用://FuncWithDefaultPara(false);// 03.使用命名参数调用FuncWithDefaultPara(gender: false);Console.ReadKey();}    

  通过调试,可以得到如下结果:

  通过前面的分析,我们可以分析出,使用命名参数被编译之后还是会生成指定参数值的调用:

四、自动初始化器

4.1 属性初始化器

  (1)在开发中,我们经常会这些为new出来的对象设置属性:

        static void InitialPropertyFunc(){Person p = new Person() { Name = "小强", Age = 18 };Console.WriteLine("Name:{0}-Age:{1}", p.Name, p.Age);}    

  (2)但是,经过编译后我们发现原来还是以前的方式:先new出来,然后一个属性一个属性地赋值。这里,编译器首先生成了一个临时对象g_initLocal0,然后为其属性赋值,最后将g_initLocal0这个对象的地址传给要使用的对象p。

4.2 集合初始化器

  (1)在开发中,我们经常在一个集合的实例化中,就为其初始化:

        static void InitialCollectionFunc(){List<Person> personList = new List<Person>(){new Person(){Name="小强",Age=10},new Person(){Name="小王",Age=15},new Person(){Name="小李",Age=18}};foreach(Person person in personList){Console.WriteLine("Name:{0}-Age:{1}", person.Name, person.Age);}}

  (2)经过上面的集合初始化器我们了解到编译器还是会编译成原来的方式,即先new出来,为其分配了内存空间之后,再一个一个地为其属性赋值。那么,在集合的初始化中我们也可以大胆地猜测,编译器也是做了以上的优化工作:即先将每个对象new出来,然后一个一个地为属性赋值,最后调用集合的Add方法将其添加到集合中。于是,我们还是反编译一下,一探究竟:

Edison Chou相关推荐

  1. 大志非才不就,大才非学不成—我的博文资源汇总

    零.苦逼码农的自我修养系列 PS:为什么此部分序号是零而不是一?因为这是作为一个码农所应该具有的基础之中的基础,要想做个好码农,此部分还得花大力气啃书啊,这决定了我们看待计算机程序的高度. 0.1 数 ...

  2. mapreduce 丢数据_大数据之MapReduce详解

    1.什么是Map/Reduce,看下面的各种解释: (1)MapReduce是hadoop的核心组件之一,hadoop要分布式包括两部分,一是分布式文件系统hdfs,一部是分布式计算框,就是mapre ...

  3. C#委托与事件学习笔记

    今天跟随视频学习了一下C#中最重要的一些概念之委托与事件.老杨的视频讲的还是挺深入浅出,不过刚接触C#.NET的人还是朦朦胧胧,就像张子阳先生说的"每次见到委托和事件就觉得心里别(biè)得 ...

  4. 自己动手写一个简单的MVC框架(第二版)

    一.ASP.NET MVC核心机制回顾 在ASP.NET MVC中,最核心的当属"路由系统",而路由系统的核心则源于一个强大的System.Web.Routing.dll组件. 在 ...

  5. 大志非才不就,大才非学不成—博文资源汇总

    零.苦逼码农的自我修养系列 PS:为什么此部分序号是零而不是一?因为这是作为一个码农所应该具有的基础之中的基础,要想做个好码农,此部分还得花大力气啃书啊,这决定了我们看待计算机程序的高度. 0.1 数 ...

  6. Hadoop学习笔记—4.初识MapReduce

    一.神马是高大上的MapReduce MapReduce是Google的一项重要技术,它首先是一个 编程模型 ,用以进行大数据量的计算.对于大 数据量的计算,通常采用的处理手法就是并行计算.但对许多开 ...

  7. 【大型网站技术实践】初级篇:搭建MySQL主从复制经典架构

    一.业务发展驱动数据发展 随着网站业务的不断发展,用户量的不断增加,数据量成倍地增长,数据库的访问量也呈线性地增长.特别是在用户访问高峰期间,并发访问量突然增大,数据库的负载压力也会增大,如果架构方案 ...

  8. ASP.Net MVC开发基础学习笔记(5):区域、模板页与WebAPI初步

    http://blog.jobbole.com/85008/ ASP.Net MVC开发基础学习笔记(5):区域.模板页与WebAPI初步 2015/03/17 · IT技术 · .Net, Asp. ...

  9. 基于 Consul 实现 MagicOnion(GRpc) 服务注册与发现

    0.简介 0.1 什么是 Consul Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置. 这里所谓的服务,不仅仅包括常用的 Api 这些服务,也包括软件开发过程 ...

  10. 窥探ASP.Net MVC底层原理 实现跨越Session的分布式TempData

    Hi,guys!Long time no see! 1.问题的引出 我相信大家在项目中都使用过TempData,TempData是一个字典集合,一般用于两个请求之间临时缓存数据或者页面之间传递消息.也 ...

最新文章

  1. object转成实体对象_Object.assign 原理及其实现
  2. 深度学习中不变性是什么?平移不变性Translation Invariance、旋转/视角不变性Ratation/Viewpoint Invariance、尺度不变性Size、Illumination
  3. 电脑连接电视方法详解_电脑如何连网?——校园宽带的连接方法(详解版)
  4. python让函数抛出异常,是否有任何对象可以使str()函数在python中抛出错误或异常?...
  5. java jespa_Jespa实际运用的一点心得
  6. [react] 如何提高组件的渲染效率呢?
  7. 链表(Linked List)之单链表
  8. 514 - Rails
  9. [Flink] Flink运行报错Container released on a *lost* node
  10. mysql关联查询语句
  11. DSL(domain specific language )
  12. python转二进制_python转二进制
  13. 【Java进阶营】Java面试题收集
  14. Win10取消文件默认打开方式
  15. Win7与Win10在局域网内共享打印机
  16. Linux命令详解-hwclock
  17. DLL简述,很有意思
  18. OKR目标与关键结果法
  19. 开源Excel报表工具:jxls
  20. AST实战|手把手教你还原ob混淆:ob混淆代码特征

热门文章

  1. 【通讯录自动导入】txt格式转vcf格式
  2. 南大小百合 计算机,是我从南大小百合里搜集的。已经编辑过了
  3. 猿如意|初识CSDN的开发者工具合集
  4. Trend趋势反垃圾邮件黑名单申诉方法
  5. InnoDB Adaptive Hash Index(AHI)
  6. 当杭州为人称道的美丽遇见了华为云!
  7. linux系统取消自检,Linux 磁盘自检设置
  8. 【最佳实践】瀚高数据库安全版v4.5.8非root用户运行的安装配置
  9. 关于win10 无线网络不可用,网络适配器出现全感叹号
  10. 跨期套利交易系统策略