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

开篇:在日常的.NET开发学习中,我们往往会接触到一些较新的语法。它们相对曾经的老语法相比。做了非常多的改进,简化了非常多繁杂的代码格式,也大大降低了我们这些菜鸟码农的代码量。可是,在开心欢乐之余,我们也不禁地对编译器内部究竟为我们做了哪些事儿而感到好奇?于是。我们就借助反编译神器,去看看编译器究竟做了啥事!

事实上本篇中非常多都不算新语法。对于非常多人来说可能都是接触了非常久了,这里主要是针对.NET的老版本号来说。是一个“相对”的新语法。

/* 新语法索引 */

1.自己主动属性 Auto-Implemented Properties
2.隐式类型 var
3.參数默认值 和 命名參数
4.对象初始化器 与 集合初始化器 { }
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方法中匹配私有字段。

二、隐式类型—keyword: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 userList
where u.IsDel == 0
select 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. Key/Value之王Memcached初探:二、Memcached在.Net中的基本操作 - Edison Chou

    一.Memcached ClientLib For .Net 首先,不得不说,许多语言都实现了连接Memcached的客户端,其中以Perl.PHP为主. 仅仅memcached网站上列出的语言就有: ...

  2. C#基础篇 - 理解委托和事件

    1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托是C#中类型安全的,可以订阅一个或多个具有相同签名方法的函数指针.简单理解,委托是一种可以把函数当做参数传递的类型.很多情况下,某 ...

  3. .NET基础拾遗(3)字符串、集合和流

    Index: (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开发基础 ...

  4. 学习之路~sqh

    推荐博客 Edison Chou: Vamei: 算法∙面试专题 - 简书: 设计模式 极速理解设计模式系列[目录索引]- Caleung: Net设计模式 - 灵动生活: 宅男程序员给老婆的计算机课 ...

  5. 剑指Offer面试题:27.最小的k个数

    一.题目:最小的k个数 题目:输入n个整数,找出其中最小的k个数.例如输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 这道题是典型的TopK问题,其最简单的思路莫过于 ...

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

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

  7. Hybrid App移动应用开发初探

    一.移动App类型及其优缺点 1.1 Native App Native App(原生App)是用原生语言(Object-C/Java/C#/....)开发,用户需要下载安装的手机应用. 优点是 可以 ...

  8. Hadoop学习笔记—6.Hadoop Eclipse插件的使用

    开篇:Hadoop是一个强大的并行软件开发框架,它可以让任务在分布式集群上并行处理,从而提高执行效率.但是,它也有一些缺点,如编码.调试Hadoop程序的难度较大,这样的缺点直接导致开发人员入门门槛高 ...

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

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

随机推荐

  1. 带有public static void main方法的类,其中的成员变量必须是static的,否则main方法没法调用。除非是main里的局部变量。因为main方法就是static的啊。

    带有public static void main方法的类,其中的成员变量必须是static的,否则main方法没法调用.除非是main里的局部变量.因为main方法就是static的啊.

  2. insmod hello.ko -1 Invalid module format最简单的解决的方法

    在下也是从网上搜索到的这样的解决的方法. 遇到这样的情况后,通过dmesg看一下内核日志. 假设发现有例如以下日志.那就好办了. hello: version magic '2.6.33.3 ' sh ...

  3. [MySQL] 按年度、季度、月度、周、日统计查询

    该死的mysql没有提供unix时间戳的专门处理函数,所以,如果遇到时间分组,而你用的又是整型unix时间戳,则只有转化为mysql的其他日期类型!   FROM_UNIXTIM()将unix时间戳转 ...

  4. 通过QEMU-GuestAgent实现从外部注入写文件到KVM虚拟机内部

    本文将以宿主上直接写文件到VM内部为例讲解为何要注入以及如何实现 tag: qemu-ga, qemu guest agent, kvm, guest-file-write, inject 小慢哥的原 ...

  5. js最简单的-点击小图放大

    js最简单的-点击小图放大 标签(空格分隔): js <html> <body> <img class="imgview" src="{$v ...

  6. 1.matlab基础准备及入门

    1.1 Command Window(命令行窗口)运用入门 1 计算器的用法 2 数值变量与表达式 3. 计算结果的图形表示 代码及注释 function [ output_args ] = Unti ...

  7. .NET序列化工具Jil、Json.NET和Protobuf的简单测评

    前一段时间逛园子的时候发现有人比较了Jil.Json.NET和Protobuf的性能,一时好奇,也做了个测试,这里记录下来,以供查阅. 前期准备 依赖类库的话,可以通过Nuget在公共组件库总下载,这 ...

  8. 构建工具系列一--Travis-cli

    本文地址: http://www.cnblogs.com/blackmanba/articles/continuous-integration-tool-travis-cli.html或者http:/ ...

  9. Java标识符规范

    1.标识符用来定义包名,类名,方法名,变量名,常量名. 2.标识符必须由字母.下划线.$符号组成,不能以数字开头.不能是Java中的保留关键字.

  10. MySQL · 答疑解惑 · 备库Seconds_Behind_Master计算

    背景 在mysql主备环境下,主备同步过程如下,主库更新产生binlog, 备库io线程拉取主库binlog生成relay log.备库sql线程执行relay log从而保持和主库同步. 理论上主库 ...