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;
posted @ 2020-03-31 20:50  mosakashaka  阅读(142)  评论(0编辑  收藏  举报