零度 C#7.0 中的语言新特性
在去年,零度分享了 C#6.0 中的新特性,随着时间的推移,微软发布了 Visual Studio 2017,同时 C#7.0 发布已经有一段时间了,C#7.0 中新增了非常有用的新功能,如果您是.NET开发者,建议花些时间来了解这些新特性。
1、Out 变量
在之前版本的 C# 中,使用 out 变量,您需要先定义并申明变量,如下所示。
static void Main(string[] args) { int value; //需要预先定义 if (int.TryParse("123", out value)) { Console.WriteLine(value); } }
在 C#7.0 中,可以在传参的同时进行申明,当然也可以将 int 改成 var 匿名类型。
static void Main(string[] args) { if (int.TryParse("123", out int value)) { Console.WriteLine(value); } }
2、元组特性
当编写一个函数时,有时需要返回多个结果,而且这个结果只用一次,不必创建对象,用元组就可返回多个结果,首选需要安装 System.ValueTuple 这个库。
install-package System.ValueTuple
通过下面的方法,就可使用元组返回多个值。
public static (string v1, string v2, string v3) GetValues() { return ("零度", "编程", "官网"); }
也可以只给定类型不设置参数名来定义返回元组的方法。
public static (string, string, string) GetValues() { return ("零度", "编程", "官网"); }
可以通过以下的方式来调用元组方法。
(string a, string b, string c) = GetValues(); Console.WriteLine(a + b + c);
也可直接使用 var 匿名类型来接收元组方法的返回值,与以上调用方式等效。
var result = GetValues(); Console.WriteLine(result.v1 + result.v2 + result.v3);
3、解析方法
解析方法可以将方法返回的元组进行解析,解析方法很灵活,可以通过以下几种解析方法解析元祖。
(string first, string middle, string last) = GetValues(); Console.WriteLine(first + middle + last);
在内部使用 var 匿名类型的解析方法。
(var first, var middle, var last) = GetValues();
甚至可以将 var 放在括号外进行简写。
var (first, middle, last) = GetValues();
当然,解析方法不仅仅适用于元组,如要它拥有如下的形式,就能够使用解析方法。
public void Deconstruct(out T1 x1, ..., out Tn xn)
为类定义一个解析方法(也可以是扩展方法),需要符合如上的 Deconstruct 格式,如下所示。
public class Book { public int BookID { get; set; } public double BookPrice { get; set; } public void Deconstruct(out int id, out double price) }
在外部可以使用如下的方式调用一个类的解析方法,系统会自动推断出 book 对象 Deconstruct 方法并解析。
Book book = new Book ; (int id, double price) = book; Console.WriteLine(price);
4、模式匹配
之前,我们判断一个对象是否兼容某个类型,一般使用 is 关键字,兼容则进行隐式类型转换,然后参与运算,代码如下。
static void Main(string[] args) { object objectValue = 123; if (objectValue is int) { int intValue = (int)objectValue; int sum = intValue + 321; Console.WriteLine(sum); } }
现在,我们可以使用模式匹配特性将类型判断与转换同时进行。
static void Main(string[] args) { object objectValue = 123; if (objectValue is int intValue) { int sum = intValue + 321; Console.WriteLine(sum); } }
当然,除了 if 语句进行模式匹配外,switch 语句也支持模式匹配。
static dynamic AddValue(object value) { dynamic result=null; switch (value) { case int b: result = b++; break; case string c: result = c + "零度"; break; } return result; }
5、局部函数
在 C#7 中可以在函数内部定义函数并调用,下面的代码在 Main 函数中定义一个 Add 函数,然后调用这个函数。
static void Main(string[] args) { int Add(int value1, int value2) { return value1 + value2; } int result = Add(111, 222); Console.WriteLine(result); }
6、数值文字
为了提高数值类型的可读性,C#7 中引入数值可读性,使用此特性,在不影响最终结果的情况下,通过下划线提高数值的可读性。
static void Main(string[] args)
{ var intValue = 123_456_000_000; //表示123456000000
var hexValue = 0xAB_CD_EF; //0x表示十六进制
var binValue = 0b1010_1011_1100_1101_1110_1111; //0b表示二进制
}
既然是数值类型的分割,那么同样可用于 decimal、float 和 double 类型。
7、方法主体使用表达式
在 C#6 中定义函数时,可以将一个 Lambda 表达式作为函数主体,在 C#7 中表达式可以用在访问器、构造器和析构器。
public class Person { public void PrintName() => Console.WriteLine(firstName); private string firstName = string.Empty; public Person(string name) => firstName=name; //构造函数 ~Person() => firstName=string.Empty; //析构函数 public string FirstName { get => firstName; //GET访问器 set => firstName = value; //SET访问器 } }
8、异常表达式
之前,通过条件判断抛出指定的异常。
public void WriteString(string parameter) { if (parameter == null) { throw new ArgumentNullException(); } Console.WriteLine(parameter); }
现在,可以将异常抛出作为表达式来使用。
public void WriteString2(string parameter) { parameter = parameter?? throw new ArgumentNullException(); Console.WriteLine(parameter); }
9、将 ref 用于变量和返回值
之前,ref 关键字可将值类型通过引用进行传递,现在 ref 可以用于局部变量和返回值,以下代码将变量 x 的引用传递给 y 变量,也就是说 y 与 x 引用相同的内存地址,下面的代码输出 x 的值为 2。
static void Main(string[] args) { int x = 3; ref int y = ref x; y = 2; Console.WriteLine(x); }
在方法返回值中使用 ref 引用变量。
static ref int GetValue(int[] array, int index) => ref array[index];
以上函数可以使用如下的方式进行调用。
int[] array = { 1, 2, 3, 4, 5 }; ref int x = ref GetValue(array, 2);