C# 8.0 的新特性

流过昼夜 提交于 2020-02-13 07:50:14

 



使用VS2019体检C#8.0新功能:

编辑.csproj文件,添加如下代码

<PropertyGroup>
  <LangVersion>preview</LangVersion>
 </PropertyGroup>

可空引用类型(Nullable reference types)

引用类型将会区分是否可空,可以从根源上解决 NullReferenceException。

复制代码
#nullable enable
        void M(string? s)
        {
            Console.WriteLine(s.Length); // 产生警告:可能为 null
            if (s != null)
            {
                Console.WriteLine(s.Length); // Ok
            }
        }
#nullable disable
复制代码

异步流(Async streams)

考虑到大部分 Api 以及函数实现都有了对应的 async版本,而 IEnumerable<T>和 IEnumerator<T>还不能方便的使用 async/await就显得很麻烦了。
  但是,现在引入了异步流,这些问题得到了解决。
  我们通过新的 IAsyncEnumerable<T>和 IAsyncEnumerator<T>来实现这一点。同时,由于之前 foreach是基于IEnumerable<T>和 IEnumerator<T>实现的,因此引入了新的语法await foreach来扩展 foreach的适用性。

复制代码
async Task<int> GetBigResultAsync()
{
    var result = await GetResultAsync();
    if (result > 20) return result; 
    else return -1;
}

async IAsyncEnumerable<int> GetBigResultsAsync()
{
    await foreach (var result in GetResultsAsync())
    {
        if (result > 20) yield return result; 
    }
}
复制代码

范围和下标类型(Ranges and indices)

C# 8.0 引入了 Index 类型,可用作数组下标,并且使用 ^ 操作符表示倒数。
  不过要注意的是,倒数是从 1 开始的。

Index i1 = 3;  // 下标为 3
Index i2 = ^4; // 倒数第 4 个元素
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"


除此之外,还引入了 “..” 操作符用来表示范围(注意是左闭右开区间)。

var slice = a[i1..i2]; // { 3, 4, 5 }

关于这个下标从 0 开始,倒数从 1 开始,范围左闭右开。

模式匹配表达式(Switch expressions )

典型的模式匹配语句,只不过没有用“match”关键字,而是沿用了了“switch”关键字

复制代码
object figure = "";
var area = figure switch
{
    Line _ => 0,
    Rectangle r => r.Width * r.Height,
    Circle c => c.Radius * 2.0 * Math.PI,
    _ => throw new UnknownFigureException(figure)
};
复制代码

C# 8.0中的模式匹配相对C# 7.0来说有了进一步的增强,对于如下类:

复制代码
class Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y) => (X, Y) = (x, y);
public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}
复制代码

首先来看C# 7.0中一个经典的模式匹配示例:

复制代码
static string Display(object o)
{
    switch (o)
    {
        case Point p when p.X == 0 && p.Y == 0:
            return "origin";
        case Point p:
            return $"({p.X}, {p.Y})";
        default:
            return "unknown";
    }
}
复制代码

在C# 8.0中,它有更加精简的写法。

Switch表达式

在C# 8.0中,可以利用新的switch方式成模式匹配:

static string Display(object o) => o switch
{
    Point p when p.X == 0 && p.Y == 0 => "origin",
    Point p                           => $"({p.X}, {p.Y})",
    _                                 => "unknown"
};

它利用一条switch语句完成了模式匹配,第一样看上去要简洁一些。不过,它还有更多更简单的写法。

Property patterns

可以直接通过在属性上指定值作为判定条件,

static string Display(object o) => o switch
{
    Point { X: 0, Y: 0 } => "origin",
    Point p              => $"({p.X}, {p.Y})",
    _                    => "unknown"
};

也可以将属性值传递出来。

static string Display(object o) => o switch
{
    Point { X: 0, Y: 0 }         => "origin",
    Point { X: var x, Y: var y } => $"({x}, {y})",
    _                            => "unknown"
};

Positional patterns

利用解构函数,可以写出更加精简的表达式。

static string Display(object o) => o switch
{
    Point(0, 0)         => "origin",
    Point(var x, var y) => $"({x}, {y})",
    _                   => "unknown"
};

如果没有类型转换,则可以写得更加简单了:

static string Display(Point o) => o switch
{
    (0, 0)         => "origin",
    (var x, var y) => $"({x}, {y})"
};

非空判断

如果只是判断空和非空,则有最简单的模式:

{ }  => o.ToString(),
null => "null"

Tuple patterns

也支持直接对ValueTuple进行模式匹配,用起来非常灵活。

复制代码
static State ChangeState(State current, Transition transition, bool hasKey) =>
    (current, transition, hasKey) switch
{
    (Opened, Close, _)     => Closed,
    (Closed, Open,  _)     => Opened,
    (Closed, Lock, true)   => Locked,
    (Locked, Unlock, true) => Closed,
    _ => throw new InvalidOperationException($"Invalid transition")
};
复制代码

递归模式语句(recursive patterns)

现在可以这么写了(patterns 里可以包含 patterns)

复制代码
IEnumerable<string> GetEnrollees()
{
    foreach (var p in People)
    {
        if (p is Student { Graduated: false, Name: string name }) yield return name;
     }
 }
复制代码
0
0
« 上一篇: 2019年6月13日单词
» 下一篇: 随机数Random

 



使用VS2019体检C#8.0新功能:

编辑.csproj文件,添加如下代码

<PropertyGroup>
  <LangVersion>preview</LangVersion>
 </PropertyGroup>

可空引用类型(Nullable reference types)

引用类型将会区分是否可空,可以从根源上解决 NullReferenceException。

复制代码
#nullable enable
        void M(string? s)
        {
            Console.WriteLine(s.Length); // 产生警告:可能为 null
            if (s != null)
            {
                Console.WriteLine(s.Length); // Ok
            }
        }
#nullable disable
复制代码

异步流(Async streams)

考虑到大部分 Api 以及函数实现都有了对应的 async版本,而 IEnumerable<T>和 IEnumerator<T>还不能方便的使用 async/await就显得很麻烦了。
  但是,现在引入了异步流,这些问题得到了解决。
  我们通过新的 IAsyncEnumerable<T>和 IAsyncEnumerator<T>来实现这一点。同时,由于之前 foreach是基于IEnumerable<T>和 IEnumerator<T>实现的,因此引入了新的语法await foreach来扩展 foreach的适用性。

复制代码
async Task<int> GetBigResultAsync()
{
    var result = await GetResultAsync();
    if (result > 20) return result; 
    else return -1;
}

async IAsyncEnumerable<int> GetBigResultsAsync()
{
    await foreach (var result in GetResultsAsync())
    {
        if (result > 20) yield return result; 
    }
}
复制代码

范围和下标类型(Ranges and indices)

C# 8.0 引入了 Index 类型,可用作数组下标,并且使用 ^ 操作符表示倒数。
  不过要注意的是,倒数是从 1 开始的。

Index i1 = 3;  // 下标为 3
Index i2 = ^4; // 倒数第 4 个元素
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"


除此之外,还引入了 “..” 操作符用来表示范围(注意是左闭右开区间)。

var slice = a[i1..i2]; // { 3, 4, 5 }

关于这个下标从 0 开始,倒数从 1 开始,范围左闭右开。

模式匹配表达式(Switch expressions )

典型的模式匹配语句,只不过没有用“match”关键字,而是沿用了了“switch”关键字

复制代码
object figure = "";
var area = figure switch
{
    Line _ => 0,
    Rectangle r => r.Width * r.Height,
    Circle c => c.Radius * 2.0 * Math.PI,
    _ => throw new UnknownFigureException(figure)
};
复制代码

C# 8.0中的模式匹配相对C# 7.0来说有了进一步的增强,对于如下类:

复制代码
class Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y) => (X, Y) = (x, y);
public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}
复制代码

首先来看C# 7.0中一个经典的模式匹配示例:

复制代码
static string Display(object o)
{
    switch (o)
    {
        case Point p when p.X == 0 && p.Y == 0:
            return "origin";
        case Point p:
            return $"({p.X}, {p.Y})";
        default:
            return "unknown";
    }
}
复制代码

在C# 8.0中,它有更加精简的写法。

Switch表达式

在C# 8.0中,可以利用新的switch方式成模式匹配:

static string Display(object o) => o switch
{
    Point p when p.X == 0 && p.Y == 0 => "origin",
    Point p                           => $"({p.X}, {p.Y})",
    _                                 => "unknown"
};

它利用一条switch语句完成了模式匹配,第一样看上去要简洁一些。不过,它还有更多更简单的写法。

Property patterns

可以直接通过在属性上指定值作为判定条件,

static string Display(object o) => o switch
{
    Point { X: 0, Y: 0 } => "origin",
    Point p              => $"({p.X}, {p.Y})",
    _                    => "unknown"
};

也可以将属性值传递出来。

static string Display(object o) => o switch
{
    Point { X: 0, Y: 0 }         => "origin",
    Point { X: var x, Y: var y } => $"({x}, {y})",
    _                            => "unknown"
};

Positional patterns

利用解构函数,可以写出更加精简的表达式。

static string Display(object o) => o switch
{
    Point(0, 0)         => "origin",
    Point(var x, var y) => $"({x}, {y})",
    _                   => "unknown"
};

如果没有类型转换,则可以写得更加简单了:

static string Display(Point o) => o switch
{
    (0, 0)         => "origin",
    (var x, var y) => $"({x}, {y})"
};

非空判断

如果只是判断空和非空,则有最简单的模式:

{ }  => o.ToString(),
null => "null"

Tuple patterns

也支持直接对ValueTuple进行模式匹配,用起来非常灵活。

复制代码
static State ChangeState(State current, Transition transition, bool hasKey) =>
    (current, transition, hasKey) switch
{
    (Opened, Close, _)     => Closed,
    (Closed, Open,  _)     => Opened,
    (Closed, Lock, true)   => Locked,
    (Locked, Unlock, true) => Closed,
    _ => throw new InvalidOperationException($"Invalid transition")
};
复制代码

递归模式语句(recursive patterns)

现在可以这么写了(patterns 里可以包含 patterns)

复制代码
IEnumerable<string> GetEnrollees()
{
    foreach (var p in People)
    {
        if (p is Student { Graduated: false, Name: string name }) yield return name;
     }
 }
复制代码
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!