局部函数
有时候,一个辅助函数可以在一个独立函数内部起作用。现在,你可以以一个局部函数的方式在其它函数内部声明这样的函数:
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
这是绕过占位符进入大数据结构的好方法。例如,一个游戏也许会将它的数据保存在大型预分配的阵列结构中(为了避免垃圾回收机制暂停)。方法可以将直接引用返回成一个结构,通过它的调用者可以读取和修改它。










