Note:元组依赖于一组基本类型,却不包括在 Preview 4 中。为了使该特性工作,你可以通过 NuGet 获取它们:
•右键单击 Solution Explorer 中的项目,然后选择“管理的NuGet包......”
•选择“Browse”选项卡,选中“Include prerelease”,选择“nuget.org”作为“Package source”
•搜索“System.ValueTuple”并安装它。
解构
消耗元组的另一种方法是将解构它们。一个解构声明是一个将元组(或其他值)分割成部分并单独分配到新变量的语法:
(string first, string middle, string last) = LookupName(id1); // deconstructing declaration
WriteLine($"found {first} {last}.");
在解构声明中,您可以使用 var 来声明单独的变量:
(var first, var middle, var last) = LookupName(id1); // var inside
或者将一个单独的 var 作为一个缩写放入圆括号外面:
var (first, middle, last) = LookupName(id1); // var outside
你也可以使用解构任务来解构成��有的变量
(first, middle, last) = LookupName(id2); // deconstructing assignment
解构不只是应用于元组。任何的类型都可以被解构,只要它具有(实例或扩展)的解构方法:
public void Deconstruct(out T1 x1, ..., out Tn xn) { ... }
输出参数构成了解构结果中的值。
(为什么它使用了参数,而不是返回一个元组?这是为了让你针对不同的值拥有多个重载)。
class Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y) { X = x; Y = y; }
public void Deconstruct(out int x, out int y) { x = X; y = Y; }
}
(var myX, var myY) = GetPoint(); // calls Deconstruct(out myX, out myY);
这是一种常见的模式,以一种对称的方式包含了构建和解构。
对于输出变量,我们计划在解构中加入通配符,来化简你不关心的变量:
(var myX, *) = GetPoint(); // I only care about myX
Note:通配符是否会出现在C#7.0中,这仍是未知数。
局部函数
有时候,一个辅助函数可以在一个独立函数内部起作用。现在,你可以以一个局部函数的方式在其它函数内部声明这样的函数:
public int Fibonacci(int x)
{
if (x < 0) throw new ArgumentException("Less negativity please!", nameof(x));
return Fib(x).current;
(int current, int previous) Fib(int i)
{
if (i == 0) return (1, 0);
var (p, pp) = Fib(i - 1);
return (p + pp, p);
}
}
闭合范围内的参数和局部变量在局部函数的内部是可用的,就如同它们在 lambda 表达式中一样。
举一个例子,迭代的方法实现通常需要一个非迭代的封装方法,以便在调用时检查实参。(迭代器本身不启动运行,直到 MoveNext 被调用)。局部函数非常适合这样的场景:
public IEnumerable<T> Filter<T>(IEnumerable<T> source, Func<T, bool> filter)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (filter == null) throw new ArgumentNullException(nameof(filter));
return Iterator();
IEnumerable<T> Iterator()
{
foreach (var element in source)
{
if (filter(element)) { yield return element; }
}
}
}
如果迭代器有一个私有方法传递给过滤器,那么当其它成员意外的使用迭代器时,迭代器也变得可用(即使没有参数检查)。此外,还会采取相同的实参作为过滤器,以便替换范围内的参数。
注意:在 Preview 4,局部函数在调用之前,必须被声明。这个限制将会被松开,以便使得局部函数从定义分配中读取时,能够被调用。
文字改进
C#7.0 允许 _ 出现,作为数字分隔号:
var d = 123_456;
var x = 0xAB_CD_EF;
你可以将 _ 放入任意的数字之间,以提高可读性,它们对值没有影响。
此外,C#7.0 引入了二进制文字,这样你就可以指定二进制模式而不用去了解十六进制。
var b = 0b1010_1011_1100_1101_1110_1111;
引用返回和局部引用
就像在 C# 中通过引用来传递参数(使用引用修改器),你现在也可以通过引用来返回参数,同样也可以以局部变量的方式存储参数。
public ref int Find(int number, int[] numbers)
{
for (int i = 0; i < numbers.Length; i++)
{
if (numbers[i] == number)
{
return ref numbers[i]; // return the storage location, not the value
}
}
throw new IndexOutOfRangeException($"{nameof(number)} not found");
}
int[] array = { 1, 15, -39, 0, 7, 14, -12 };
ref int place = ref Find(7, array); // aliases 7's place in the array
place = 9; // replaces 7 with 9 in the array
WriteLine(array[4]); // prints 9