C#6.0新特性笔记
Getter专属赋值
可以在构造函数中,给只有get的属性赋初始值。
class Point { public int x { get; } public Point() { x = 1; } }
自动属性初始化
可以给自动属性,赋初始化值
class Point { public int x { get; set; } = 1; }
全局静态
可以设置全局静态,然后直接写静态类的方法。
using static System.Console; namespace FxApp { internal class Program { private static void Main(string[] args) { Read(); } } }
全局枚举
可以设置全局枚举,然后直接写枚举属性
using static System.ConsoleColor; namespace FxApp { internal class Program { private static void Main(string[] args) { var color = Red; } } }
字符串插入
可以使用$来进行字符串插入,替代string.format。全局变量或参数都可插入
internal class Program { public static int x { get; set; } private static void Main(string[] args) { string text = $"{x},{args}"; } }
Expression bodied
可以使用箭头表达式来给方法或属性提供表达式体。注意,表达式体不能有括号。
internal class Program { public string Name => "Chenxy"; public int Sum(int x, int y) => x + y; private static void Main(string[] args) { Program po = new Program(); Console.WriteLine(po.Name); //Chenxy Console.WriteLine(po.Sum(1, 2)); //3 Console.Read(); } }
索引值初始化
可以给集合赋初始化值
internal class Program { IDictionary<int, string> dict = new Dictionary<int, string>() { [1] = "first", [2] = "second" }; private static void Main(string[] args) { Program po = new Program(); foreach (var item in po.dict) { Console.WriteLine(item.Value); } Console.Read(); } }
?.运算符
空值运算符,可以简化判断null的工作
public class Point { public int x { get; } = 1; } internal class Program { public static void Main(string[] args) { Point po = null; //C# 5.0 写法 if (po != null) { Console.WriteLine(po.x); } //C# 6.0 写法 Console.WriteLine(po?.x); Console.Read(); } }
nameof表达式
为了防止重构时忘记修改,可以使用nameof来将一个常量和变量绑定。变量改变时,常量随之改变
internal class Program { public static void Main(string[] args) { string error = string.Empty; //C# 5.0 Console.WriteLine("error"); //error //C# 6.0 Console.WriteLine(nameof(error)); //error Console.Read(); } }
在此代码中,如果改变 error名称,但不修改nameof(error)名称。则会在编译时提示
异常过滤器
可以在try catch中进行判断,来控制是否进入catch。如果hideError为false,则不会进入catch。
public static void Main(string[] args) { bool hideError = false; try { throw new Exception(); } catch (Exception ex) when (hideError) { Console.WriteLine("异常已抛出"); } Console.Read(); }
catch和finally中使用await
可以在catch 和 finally中,使用await。
internal class Program { public async static void Main(string[] args) { HttpClient client = null; try { var result = await client.GetAsync(""); } catch (Exception ex) { var result = await client.GetAsync(""); } finally { var result = await client.GetAsync(""); } Console.Read(); } }
Visual Studio 2017
https://zhuanlan.zhihu.com/p/25626653
C#7.0新特性笔记
Out
原先代码中,如果使用Out参数,需要预先定义变量。而C#7中,则可以直接在输出参数中定义变量。
我们也可以直接在参数中,定义var隐式变量。
//VS2015 static void Main(string[] args) { int x; OutMethod(out x); WriteLine($"X:{x}"); Read(); } //VS2017 static void Main(string[] args) { OutMethod(out int x); WriteLine($"X:{x}"); Read(); }
解构
在调用函数中,可以将参数组合封装到一个类的构造函数中。但是无法通过对象解构成各个组成部分。
通过解构函数,则可以完成这个事情。解构函数可以返回对象的具体确定组件。
例如,我们模拟一个参数组合
public class PathInfo { public string DirectoryName { get; set; } public string FileName { get; set; } public string Extension { get; set; } public PathInfo(string dname, string fname, string extension) { DirectoryName = dname; FileName = fname; Extension = extension; } }
我们需要从nuget加载 System.ValueTuple这样,才可以使用元组进行解构。
我们如果要使用解构,就需要先定义一个解构函数:Desconstruct
public void Deconstruct(out string dname, out string fname, out string extension) { dname = DirectoryName; fname = FileName; extension = Extension; }
需要注意的是,必须使用out,名称可以不一样,他是按照参数顺序返回的。
接下来,现在我们使用解构函数来调用。我们展示三种示例,来进行调用测试。
class Program { static void Main(string[] args) { PathInfo info = new PathInfo("d", "f", "e"); //示例一:参数内声明。也可以使用var (string dname, string fname, string extension) = info; WriteLine($"{dname},{fname},{extension}"); //示例二:声明值初始化。分配现有变量 string dname1, fname1, extension1 = null; (dname1, fname1, extension1) = info; WriteLine($"{dname1},{fname1},{extension1}"); //示例三:隐式变量声明 var (dname2, fname2, extension2) = info; WriteLine($"{dname2},{fname2},{extension2}"); Read(); } }
跟元组类似的写法,会给每个值进行赋值,只要顺序对应即可。
注意:在示例三种,我们在括号外面声明的var,这样括号内的三个变量类型必须一致。
解构函数,要求圆括号内至少有两个输出变量。如果只有一个是不起作用的,例如
(string dname) = info; //这样是不行的。Deconstruct,如果只有一个方法是不行的。
Deconstruct,可以进行重载。使用的时候,会自动找对应的重载方法。
元组
通过元组可以从一个方法中返回多个值。在C#7.0之前,我们会定义输出参数或者对象集合。
我们可以通过两种方式,来定义元祖。
1.不使用参数名,只填写参数类型。这样会返回 Item1,2,3
2.使用参数名,并填写参数类型。这样会返回Item.参数名
3.直接使用参数名。和2一个效果,但是方便一些。
class Program { static void Main(string[] args) { PathInfo info = new PathInfo(); var item = info.getEmpInfo(); WriteLine($"{item.Item1},{item.Item2},{item.Item3}"); var item2 = info.getEmpInfo2(); WriteLine($"{item2.a},{item2.b},{item2.c}"); var item3 = info.getEmpInfo3(); WriteLine($"{item3.a},{item3.b},{item3.c}"); Read(); } } public class PathInfo { // 不使用参数名,返回Item1,2,3 public (string, string, string) getEmpInfo() { return ("a", "b", "c"); } // 使用参数名,返回定义参数名 public (string a, string b, string c) getEmpInfo2() { return ("a", "b", "c"); } // 使用指定元素 public (string a, string b, string c) getEmpInfo3() { return (a: "a", b: "b", c: "c"); } }
模式匹配
有时候基类会派生一些子类。我们如果要对每个类实施Eject方法。可以使用多个选择来实现。
AS运算符
使用AS运算符转换并赋值
检查结果是否为null
执行Eject操作
IS运算符
使用IS运算符检查类型
转换类型并为其赋值
执行Eject操作
Cast
显示转换赋值
捕捉可能的异常
执行操作
上述这些方法存在的问题都是语法相当冗长,总要为转换的类提供多个语句
具有IS表达式的模式匹配
C#7.0提供模式匹配,用作一种将测试和赋值合并为单个操作方法。
举个例子,我们在C#7以前,进行类型转换如下。我们使用IS运算符
class Program { static void Main(string[] args) { Eject(new Woman()); Read(); } static void Eject(People peo) { if (peo is Woman) { Woman wo = (Woman)peo; wo.Con(); } } } class People {} class Woman : People { public void Con() { WriteLine("This is Woman"); } }
可以看出来,我们需要进行 is,然后进行转换。现在我们使用模式匹配来实现上面代码
static void Eject(People peo) { if (peo is Woman wo) { wo.Con(); } }
模式匹配将测试和赋值合并成一个操作了。
Switch语句的模式匹配
使用Switch可以在多个可转换的兼容类型之间时更加重要。
如果我们想添加一个附加条件,可以使用when附加在case子句。
static void Eject(People peo) { switch (peo) { case Woman wo when wo != null: wo.Con(); break; case People pe: break; default: break; } }
本地函数
C#7.0允许在一个成员内部完全声明本地函数。来替代Action和Func.
本地函数的作用于域,进在周围函数的内部。关于本地函数有以下几个注意点
1.不允许修饰符
2.不支持重载
3.本地函数可以访问所有变量,包括局部变量
4.本地函数存在整个方法范围内,声明不分前后
bool IsPalindrome(string text) { if (string.IsNullOrWhiteSpace(text)) return false; bool LocalIsPalindrome(string target) { target = target.Trim(); // Start by removing any surrounding whitespace. if (target.Length <= 1) return true; else { return char.ToLower(target[0]) == char.ToLower(target[target.Length - 1]) && LocalIsPalindrome( target.Substring(1, target.Length - 2)); } } return LocalIsPalindrome(text); }
通过引用返回
通过ref可以将参数传递给函数来进行更新。
C#7.0除了ref参数外,还可以通过函数返回一个引用。
class Program { static void Main(string[] args) { int[] result = { 1, 2, 3 }; ref int a = ref Find(result); a += 1; WriteLine(result[0]); Read(); } static ref int Find(int[] sum) { return ref sum[0]; } }
文本改进
C#7.0可以通过下划线来提高可读性。可以放在二进制、十进制、十六进制数字的任意位置
long LargestSquareNumberUsingAllDigits = 0b0010_0100_1000_1111_0110_1101_1100_0010_0100; // 9,814,072,356 long MaxInt64 { get; } = 9_223_372_036_854_775_807; // Equivalent to long.MaxValue
Throw表达式
可以在表达式中,直接抛出异常
public string getEmpInfo( string EmpName) { string[] empArr = EmpName.Split(","); return (empArr.Length > 0) ? empArr[0] : throw new Exception("Emp Info Not exist"); }
异步返回类型
编译器不限制异步方法必须返回 void、Task、Task<T>。
任何可以继承 GetAwaiter 方法的都可以进行返回。
但是,此方法我验证不通过。故此暂时不考虑这个特性。
Expression bodied 增强
C#7.0里面把箭头函数表达式,进行了增强。
我们可以直接在构造函数、析构函数中使用
public class Point { public Point() => WriteLine("构造"); ~Point() => WriteLine("释放"); }
$