# 语法糖 ## 自动属性 未简化:手写私有变量+公有属性 ```csharp private string _myName; public string MyName { get { return _myName; } set { _myName = value; } } ``` 简化:声明空属性,编译器自动生成对应私有成员字段。 ```csharp public string MyName { get; set; } ``` ## 参数默认值和命名参数 使用的指导原则: 1、可以为方法和有参属性指定默认值 2、有默认值的参数,必须定义在没有默认值的参数之后 3、默认参数必须是常量 4、ref和out参数不能指定默认值 ```csharp public class User { public string Name { get; set; } public int Age { get; set; } // 自动属性默认初始化 public string Address { get; set; } = "江苏"; // 设置参数默认值(age) public User(string name, string address, int age = 18) { this.Name = name; this.Age = age; this.Address = address; } } // 命名参数(指定address参数) User user = new User("小王",address:"苏州"); ``` ## 类型实例化 ```csharp // 使用对象初始化器:{},使用对象初始化器,必须提供一个无参的构造函数,可以只给部分属性初始化 User user = new User() { Name = "小王", Age = 18, Address = "苏州", }; ``` ## List集合 ### 初始化List集合的值 ```csharp // 简化之前 List listString = new List(); listString.Add("小王"); listString.Add("小贤"); // 简化后 List listString = new List() { "小王", "小贤", }; ``` ### 取List中的值 ```csharp // 简化之前 foreach (string str in listString) { Console.WriteLine(str); } // 简化之后 listString.ForEach(s => Console.WriteLine(s)); ``` ## 隐式类型(var) 程序员在声明变量时可以不指定类型,由编译器根据值来指定类型 var定义变量有以下四个特点: 1、必须在定义时初始化 2、一旦初始化完成,就不能再给变量赋与初始值不同类型的值了 3、var要求是局部变量 4、使用var定义变量和object不同,它在效率上和使用强类型方式定义变量完全一样 ```csharp // 1.隐式类型在定义时必须初始化 //var name; 报错 var name = "小王"; // 2.可以用同类型的其他隐式类型变量来初始化新的隐式类型变量 var age = 18; var ageTwo = age; // 3.也可以用同类型的字面量来初始化隐式类型变量 var name = "小王"; name = "小贤"; // 4.隐式类型局部变量还可以初始化数组而不指定类型 var array = new List() { "小王","小贤",}; ``` ## 扩展方法 扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用。 **注意:定义扩展方法的类必须和使用地方的类命名空间相同(如果不同命名空间记得先引入命名空间)** ```csharp // 为string 添加一个扩展方法(判断string是否为空||null) public static bool IsEmpty(this string str) { return string.IsNullOrEmpty(str); } ``` ## 匿名类型(Anonymous type) 匿名类型提供了一种方便的方法,可用来将一组只读属性封装到单个对象中,而无需首先显式定义一个类型。 类型名由编译器生成,并且不能在源代码级使用。 每个属性的类型由编译器推断。可通过使用 new 运算符和对象初始值创建匿名类型。 匿名类型的限制: 1、匿名类型不支持事件、自定义方法和自定义重写 2、匿名类型是隐式封闭的 3、匿名类型的实例创建只使用默认构造函数 4、匿名类型没有提供可供控制的类名称(使用var定义的) ```csharp var unUser = new { Name = "小王", Age = 18, Phone = "123456" }; ``` ## 匿名方法(Anonymous methods) 普通方法定义方式,因为方法的存在是为了复用一段代码,所以一般会给方法取个名字,这个方法的引用就可以通过“方法名”调用 匿名方法: 但是有的方法,不需要复用,仅仅是使用一次就够了,所以不需要方法名,这种方法就叫做匿名方法。 匿名方法必须结合委托使用。(潜在的意思就是:尽管没有方法名了,但方法的指针还是存放了某个委托对象中) 注意: 1、在编译后,会为每个匿名方法创建一个私有的静态方法,然后将此静态方法传给委托对象使用 2、匿名方法编译后会生成委托对象,生成方法,然后把方法装入委托对象,最后赋值给声明的委托变量 3、匿名方法可以省略参数,编译的时候会自动为这个方法按照委托签名的参数添加参数 ## dynamic动态对象 .net4.0中引入了一个新类型 dynamic.该类型是一种静态类型,但类型为 dynamic 的对象会跳过静态类型检查.大多数情况下,该对象就像具有类型 object 一样.在编译时,将假定类型化为 dynamic 的元素支持任何操作 ```csharp dynamic dy = "string"; Console.WriteLine(dy.GetType()); //输出:System.String dy = 100; Console.WriteLine(dy.GetType()); //输出:System.Int32 ``` ## 字符串嵌入值 c#6.0后新增的特性 $,用于代替string.Format("") ```csharp // 之前 var str=string.Format("时间:{0}", DateTime.Now); // 改进 var str=$"时间:{DateTime.Now}"; ``` ## 问号(?)表达式 ### 可空类型修饰符(?) 引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空 ```csharp string str = null;// 正确 //int i = null; // 错误 ``` 为了使值类型也可为空,就可以使用可空类型,,可以使用 Nullable泛型结构来定义,但是微软为了语法更简洁,提供了?运算符来定义可空类型,二者效果是一样的,基本语法如 下: 值类型? 变量名=值; ```csharp // 这二种定义可空类型的方法都是对的,但是?更简洁 Nullable intOne = 5; int? intTwo = 5; ``` ### 三元(运算符)表达式(?: ) ```csharp int gender = 1; string str = gender == 1 ? "男" : "女"; ``` ### 合并运算符(??) 合并运算符使用??表示,当进行转换时,如果可空类型的值为 null,则使用默认值 ```csharp int? intA = null; int intB = 100; intB = intA ?? 0;//此处intA为null,则将0赋值给intB int? intC = 500; intB = intC ?? 0;//此处intC不为null,则将500赋值给intB ``` ### NULL检查运算符(?.) C# 6.0中,引入了 ?. 的运算符,可帮助编写更少的代码来处理 null 检查 如果对象为NULL,则不进行后面的获取成员的运算,直接返回NULL ```csharp string str = "abcd"; string s1 = null; Console.WriteLine(str?.Length);// 输出 4 Console.WriteLine(s1?.Length);// 输出空 ``` ### "?[]"运算符 这个我还没有用过,网上例子 ```csharp int? first = customers?[0].Orders.Count(); ``` ## 使用完毕后自动释放资源(Using || try finally) 为了节约资源,每次使用完毕后都要释放掉资源,其中可以使用Using和try finally来进行释放资源操作。需要注意的是使用Using释放资源的对象都必须继承IDisposable接口(MSDN) ```csharp // try finally 写法 SqlConnection conn = null; try { conn = new SqlConnection("数据库连接字符串"); conn.Open(); } finally { conn.Close(); conn.Dispose(); } // Using写法 using (SqlConnection conn=new SqlConnection("数据库连接字符串")) { conn.Open(); } ``` ## 元组 之所以将元组放在第一位,是因为它对C#编程体验的提升实在是太大了。元组这个概念在以前就已经被引入了C#,只不过它是通过一个名为Tuples的泛型类来实现的。使用这个泛型类虽然可以达到类似元组的效果,但是可读性实在是太差了,完全就没有办法通过代码理解函数返回的到底是什么,本应该是人类友好的参数名不得不变成Item1、Item2这样的抽象名称。也正是因为如此,一般的开发者更倾向于自己定义一个结构来用于返回多个值。 C#7.0引入的元组语法能让你更优雅地完成这一任务。你可以通过这种方式来表示一个函数的返回值类型: ```csharp public (string name, int age, int height) WhoAmI() { return ("码农", 19, 175); } //在外部代码里,你可以这样来访问这个元组类型包含的多个值: var res = WhoAmI(); Console.WriteLine(res.name); Console.WriteLine(res.age); Console.WriteLine(res.height); ``` 这可比先前C#版本里,用Item1、Item2、Item3来表示元组元素的方法直观多了,你也不必写着写着就要回去查看自己的函数返回的顺序是怎样的了。 当然,如果你不想用类型推断和匿名元组类型的话,你还能这样解构一个元组: ```csharp (string name, int age, int height) = WhoAmI(); Console.WriteLine(name); Console.WriteLine(age); Console.WriteLine(height); ``` ## 局部函数 现在可以在函数里定义函数了,而且通常这个函数里的函数只能在外层函数里访问。直接上例子: ```csharp static void main(string[] args) { int add(int a, int b) { return a + b; } int first = 2; int second = 3; return add(first, second); } ``` ## 引用传递和引用返回 熟悉C#的朋友肯定知道,如果操作数是值类型,那么赋值运算符(等号)默认传递是这个类型的值;但是某些时候,我们不希望C#“自作聪明”地给我们传递值,而希望传递这个值类型的引用。在C#7.0中,微软重新利用了使用率不高的关键字ref,来表明我们要传递的是一个引用而不是值。下面是实例: ```csharp int a = 0; ref int b = ref a; b = 3; Console.Write(a); ``` 这段程序的输出结果是3,而不是通常认为的0。原因是中间的那句赋值,我们告诉C#,我们要传递的是引用,而不是值。所以后来对b的值作出修改,a的值同步改变了。 类似的,我们可以用相同的方式来让一个函数返回一个值类型的引用而不是它的值: ```csharp ref int getRefrence(int[] source, int index) { return ref source[index]; } ``` ## 模式 作为一门基于类型的语言,“封箱”和“拆箱”操作肯定深入人心。拆箱之前,我们经常需要判断这个箱子到底是什么类型的(is运算符)。以前版本的C#有个模板式的写法是:先用is运算符判断封箱类型,然后用as运算符拆箱。现在这个模板式的语法可以被缩写成这样了: ```csharp if(a is int b) { //这里的b自动是a拆箱为int的类型 } ``` 不仅如此,C#7.0把这种东西称作“模式匹配”,这让你能用switch/case语句来批量判断封箱类型,并用when关键字来限定拆箱结果的条件。比如: ```csharp switch(a) { case int b when b < 0: //操作 break; case int b: //操作 break; case string b: //操作 break; default: //操作 break; } ``` ## out变量直接定义 以前在使用带有out参数的函数的时候,我们必须先把要赋值的out参数定义好,在传递给函数。现在可以直接在传递的时候定义out参数了: ```csharp FunWithOut(out int outparam); Console.Write(outparam); ``` ## λ表达式现在支持了更多的函数 在C#7.0中,构造函数、析构函数和属性访问器也可以使用λ表达式了,比如: ```csharp public Test()=>Console.Write("已经构造类Test的实例"); ~Test()=>Console.Write("析构了Test的实例"); ``` ## 异常表达式 新增的运算符??(两个半角问号)用于在引用为null的时候抛出异常 ```csharp return a ?? throw new Exception(); ``` ## 数字里可以添加下划线增强可读性 现在C#7.0认为,1234_5678和12345678是一个数字了。也就是,数字中的下划线会被忽略。