c#6.0/7.x新特性(二)
c# 7.0
out
参数增强
允许out参数在函数调用中直接定义,out
参数不需要初始化,很多时候在传入函数时才是第一次需要被使用的。
这个增强方便不少
if (!int.TryParse(input, out int result))
{
return null;
}
return result;
Tuples
元组类型
好像在python中,是叫做元组。这是c#语言新增加的类型支持。
虽然以前也有,但是功能并不完善。
几种方式的元组创建:
var letters = ("a", "b"); //Item1, Item2
(string Alpha, string Beta) namedLetters = ("a", "b");
var alphabetStart = (Alpha: "a", Beta: "b");
作为一种类型,可以直接作为函数的参数,或者返回类型:
private static (int Max, int Min) Range(IEnumerable<int> numbers)
{
int min = int.MaxValue;
int max = int.MinValue;
foreach(var n in numbers)
{
min = (n < min) ? n : min;
max = (n > max) ? n : max;
}
return (max, min);
}
元组的解析
通过以下方式,可以直接将元组的两个参数放到max和min中:
(int max, int min) = Range(numbers);
这叫做Deconstruct
,不知道翻译成啥好。。。
丢弃变量
这个讲的是,在上一个元组解构的特性,或者是调用的函数有out
参数时,我们必须为每一个位置都定义一个变量。
而有些时候,有的变量我们其实用不到,这个时候可以使用只写变量_
占位
using System;
using System.Collections.Generic;
public class Example
{
public static void Main()
{
var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);
Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
}
private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
{
int population1 = 0, population2 = 0;
double area = 0;
if (name == "New York City") {
area = 468.48;
if (year1 == 1960) {
population1 = 7781984;
}
if (year2 == 2010) {
population2 = 8175133;
}
return (name, area, year1, population1, year2, population2);
}
return ("", 0, 0, 0, 0, 0);
}
}
// The example displays the following output:
// Population change, 1960 to 2010: 393,149
其实所有有不想要的变量又必须填一个进去时,都可以用_
,这个变量可以被任何时候赋值,但是不能用在其他场景。
格式匹配
字面不是很容易理解,含义是,根据不同条件(一般是类型)匹配某一段语句执行。
类似switch
或者is
语句,但是可以在类型后,定义一个新的变量代表类型转换后的值,在接下来使用:
IS
格式
public static int DiceSum2(IEnumerable<object> values)
{
var sum = 0;
foreach(var item in values)
{
if (item is int val)
sum += val;
else if (item is IEnumerable<object> subList)
sum += DiceSum2(subList);//否则,这里可能需要增加一个强制类型转换的语句
}
return sum;
}
switch
语句
public static int DiceSum4(IEnumerable<object> values)
{
var sum = 0;
foreach (var item in values)
{
switch (item)
{
case 0:
break;
case int val:
sum += val;
break;
case IEnumerable<object> subList when subList.Any():
sum += DiceSum4(subList);
break;
case IEnumerable<object> subList:
break;
case null:
break;
default:
throw new InvalidOperationException("unknown item type");
}
}
return sum;
}
引用类型作为返回类型
类似参数中的ref
,有时候我们需要返回值也是ref
类型,可以在返回后进行修改,c#增加了这种支持:
public static ref int Find3(int[,] matrix, Func<int, bool> predicate)
{
for (int i = 0; i < matrix.GetLength(0); i++)
for (int j = 0; j < matrix.GetLength(1); j++)
if (predicate(matrix[i, j]))
return ref matrix[i, j];
throw new InvalidOperationException("Not found");
}
ref var item = ref MatrixSearch.Find3(matrix, (val) => val == 42);
Console.WriteLine(item);
item = 24;
Console.WriteLine(matrix[4, 2]);
注意函数的返回类型,以及接受函数返回值的参数定义都必须带有ref
关键字,单独的var
关键字不能代表引用
局部函数
即在函数内定义函数。
看到有两个相对有用的地方
- 增加代码可读性,有的私有函数仅供一个函数调用,这样将其移入调用函数内部,方便理解代码
- 异步函数抛出异常:将异步操作放在内部函数中,前面的判断操作可以立刻返回
public Task<string> PerformLongRunningWork(string address, int index, string name)
{
if (string.IsNullOrWhiteSpace(address))
throw new ArgumentException(message: "An address is required", paramName: nameof(address));
if (index < 0)
throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));
return longRunningWorkImplementation();
async Task<string> longRunningWorkImplementation()
{
var interimResult = await FirstWork(address);
var secondResult = await SecondStep(index, name);
return $"The results are {interimResult} and {secondResult}. Enjoy.";
}
}
expression-bodied成员
增加了更多支持,如初始化和析构函数,getter/setter等
// Expression-bodied constructor
public ExpressionMembersExample(string label) => this.Label = label;
// Expression-bodied finalizer
~ExpressionMembersExample() => Console.Error.WriteLine("Finalized!");
private string label;
// Expression-bodied get / set accessors.
public string Label
{
get => label;
set => this.label = value ?? "Default label";
}
新增的异步返回类型ValueTask
除了Task/Void以外,ValueTask
也可以作为async函数的返回类型,相比Task,这个类型属于值类型,相比于Task
的引用类型,轻量很多
增加_
作为数字中的分割符
这是一个便于书写和阅读代码的特征,_
可以用在任何数字表达式中,仅为了看起来能够区分数字位数等信息,对数值没有任何影响:
public const int ThirtyTwo = 0b0010_0000;
public const int SixtyFour = 0b0100_0000;
public const int OneHundredTwentyEight = 0b1000_0000;
public const double AvogadroConstant = 6.022_140_857_747_474e23;
public const decimal GoldenRatio = 1.618_033_988_749_894_848_204_586_834_365_638_117_720_309_179M;