C# 7.0 语言新特性

返回C#与Java社区
0回复贴,共1页,点击数:721

动力逐浪阿林为您整理相关文章,做H5场景就上动力逐浪,咨询:544472213 18720066722

下面是关于在C#7.0语言中计划功能的说明。其中大部分功能在Visual Studio “15” Preview 4中能运行。现在是最好的试用时期,请留下你们的感受。

C#7.0语言增加了许多的新功能,促使专注于数据消费,简化代码和性能。

或许最大的特征是元组(tuples) ,使得它易于有多个结果,并从而简化代码是以对数据的形状为条件的模式匹配。


输出(out)变量


目前在C#中,使用out参数并不像我们想象中那么流畅。在使用out参数调用方法时,你首先必须声明变量传递给它。虽然你通常不会初始化这些变量(他们将通过该方法后所有被覆盖),也不能使用VAR来声明他们,但是需要指定完整的类型:


1 public voidPrintCoordinates(Point p)

2 {    int x, y; // have to "predeclare"

3    p.GetCoordinates(out x, out y);

4    WriteLine($"({x}, {y})");

5 }


在C#7.0,我们增加了Out变量,作为out参数传递的点来声明一个变量权:


1 public void PrintCoordinates(Point p)

2 {

3    p.GetCoordinates(out int x, out int y);

4    WriteLine($"({x}, {y})");

5 }


需要注意的是,变量是在封闭块范围内,所以后续可以使用它们。大多数类型的语句不建立自己的适用范围,因此out变量通常在声明中被引入到封闭范围。

注:在Preview 4中,适用范围规则更为严格:out变量的作用域为它们在声明的说法。因此,上面的例子将会在下个版本中使用。

由于out变量直接声明作为参数传递给out参数,编译器通常可以告知类型(除非有冲突的过载)。所以这是很好用VAR,而不是一个类型来声明它们:

1 p.GetCoordinates(out var x, out var y);

out参数的一个常见的用途是Try...模式,其中out参数一个boolean return表示成功,out参数进行得到的结果:


1 public void PrintStars(string s)

2 {    if (int.TryParse(s, out var i)) { WriteLine(new string('*', i)); }    else { WriteLine("Cloudy nostarstonight!");}

3 }


注:Preview 4处理的比较好的地方在于只是用if语句定义它。


计划允许“wildcards”作为out参数以及在*的形式,忽视不重要的out参数:

1 p.GetCoordinates(out int x, out *); // I only care about x

注:wildcards能否把它变成C#7.0还是个未知数。


模式匹配


C# 7.0 引入了模式的概念,抽象地说,这是一种语法成分可以用来测试一个值是否有一个一定的“形”以及在它起作用时从值里面获取到的额外信息。

下面是 C# 7.0 中关于模式的例子:

c 的常量模式(c 是C#中的一个常量表达式),用于测试输入的参数是否和 c 相等

T x 的类型模式(T 是一个类型,x 是一个标识符),用于测试输入的参数是否有类型 T,如果有,提取输入参数的值到一个 T 类型的新 x 变量中。

var x 变量模式(x 是一个标识符),通常会匹配并简单地将输入参数的值放进一个新变量 x 中

这是个开始,模式是一种新的 C# 语言元素,而且我们将来可以把它们更多地增加到 C# 中。


在 C# 7.0 中,我们正在使用模式以增强两种已存在的语言结构:

is 表达式现在在右边可以有一个模式,而不只是一个类型

case 子句在 switch 语句中现在可以通过模式匹配,而不仅仅是常量值

在将来的C#中,我们或许会增加更多能使用模式的地方。

带模式的 Is 表达式


这是一个使用带有常量模式和类型模式的 is 表达式的例子:


1 public void PrintStars(object o)

2 {    if (o is null) return;     // constant pattern "null"    if (!(o is int i)) return; // type pattern "int i"

3    WriteLine(new string('*', i));

4 }


正如你所看到的,模式变量(变量通过模式引入)与先前描述的 out 变量有些类似,他们可以在表达式中被声明,而且可以在它们最近的周围范围内被使用。也像 out 变量那样,模式变量是易变的,

注: 就像 out 变量一样,严格的范围规则适用于 Preview 4.


带模式的 Switch 语句


我们正在泛化 switch 语句,因此:

你可以在任何类型上使用 switch(不仅仅是原始类型)

可以在 case 子句中使用模式

Case 子句可以拥有额外的条件

这里是一个简单的例子:


1 switch(shape)

2 {

3    case Circle c:

4        WriteLine($"circle with radius {c.Radius}");

5        break;

6    case Rectangle s when (s.Length == s.Height):

7        WriteLine($"{s.Length} x {s.Height} square");

8        break;

9    case Rectangle r:

10        WriteLine($"{r.Length} x {r.Height} rectangle");

11        break;

12    default:

13        WriteLine("<unknown shape>");

14        break;

15    case null:

16       throw new ArgumentNullException(nameof(shape));

17 }


有几件关于这个新扩展的 switch 语句的事需要注意:

case 子句的顺序现在很重要:就像 catch 子句,case 子句不再是必然不相交的,第一个子句匹配的将被选择。因此这里重要的是上面代码中 square case 比 rectangle case 来得要早。也是和 catch 子句一样,编译器会通过标记明显不能到达的情况来帮助你。在这之前,你永远无法知道评价的顺序,所以这不是一个重大改变的特性。

默认子句总是最后被评价:即使上面代码中 null 子句是最后才来,它会在默认子句被选择前被检查。这是为了与现有 switch 语义相兼容。然而,好的做法通常会让你把默认子句放到最后。

null 子句在最后不可到达:这是因为类型模式遵循当前的 is 表达式的例子并且不会匹配空值。这保证了空值不会偶然被任何的类型模式捎来第一抢购。你必须更明确如何处理它们(或为默认子句留下他们)。

通过 case ...: 标签引入的模式变量仅存在于相对应的 switch 部分的范围内。


元组

这是常见的希望从一个方法返回多个值的做法。目前可用的选项不是最佳的:


Out 参数。使用笨拙(即便有上面描述到的提升),它们不使用异步的方法运行。

System.Tuple<...>  返回类型。使用累赘并且需要一个元组对象的分配。

为每个方法定制传输类型:大量的代码为了类型开销的目的仅是临时收集一些值

匿名类型通过返回一个 dynamic 返回类型。高性能开销并且没有静态类型检查。

为了在这方面做得更好,C# 添加了tuple types 和 tuple literals:


1 (string, string, string) LookupName(long id) // tuple return type

2 {

3    ... // retrieve first, middle and last from data storage

4    return (first, middle, last); // tuple literal

5 }


这个方法目前有效地返回三个字符串,将其作为元素在元组类型里包裹起来。

方法的调用者将会接受到一个元组,并且可以逐一访问元素。


1 var names = LookupName(id);

2 WriteLine($"found {names.Item1} {names.Item3}.");


Item1 等等,是元组元素的默认名字,并能够经常被使用。但它们不是太好描述的,因此你可以选择性地添加更好的一个。

1 (string first, string middle, string last) LookupName(long id) // tuple elements have names

现在元组的接受者拥有更多的可描述的名字用于运行:


1 var names = LookupName(id);

2 WriteLine($"found {names.first} {names.last}.");


你也可以在  tuple literals 中直接指定名字:


1 return (first: first, middle: middle, last: last); // named tuple elements in a literal


通常来说,你可以互相分配元组类型无关的名字,只要独立的元素是可以被分配的,元组类型会自如 转换成其他元组类型。特别是对于 tuple literals ,存在一些限制,这会警告或提示在常见的错误的情况下提示,例如偶然交换元素的名字。

注意:这些限制还没在 Preview 4 中实现


元组是值类型,而且他们的元素只是公开、易变的域。他们的值相等,代表这两个元组是相等的(都有相同的哈斯码)如果它们的元素都结对匹配(都有相同的哈斯码)。


这使得元组对于在多种返回值下的很多情况十分有用。举例来说,如果你需要一个有多种键的词典,使用元组作为你的键,然后一切东西就会如常工作。如果你需要在每个位置有一个有多种值的列表,使用元组,查找列表等等,程序会正常运行。


更多知识,请关注“逐浪软件”!


1楼 2016/08/29 11:33
您未登录,没有发贴权限[点此登录]