zoukankan      html  css  js  c++  java
  • .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 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);
                }
            }
  • 相关阅读:
    【CentOS 6.5】QtCreator启动时关于dbus-1的错误解决方法
    【CentOS 6.5】 Qt Creator 启动失败
    C# mvc3 mvc4 伪静态及IIS7.5配置
    【水果大全】快看,你属于哪种水果身材?
    一首《放下》最近火了!
    【情感励志】不必仰望别人,自己亦是风景
    java JDBC连接 Sqlserver 非默认的实例名问题
    后台:酒店客服订单管理.把订单编号:1290939 从取消状态恢复确认有房状态(待发单状态)
    PHP安装环境搭建
    表操作汇总(复制,删除,修改,插入,查询及数据库的复制)
  • 原文地址:https://www.cnblogs.com/rr163/p/4103900.html
Copyright © 2011-2022 走看看