
文章目录
前言
2018年11月12日微软在 MSDN 博客的 Building C# 8.01 一文发表了新的 C# 8.0 即将发布的特性,现在让我们来看一下。
新的改变
- 可空引用类型
- 异步流
- 范围和下标类型
- 接口成员的默认实现
- 模式匹配表达式和递归模式语句
- 目标类型推导
可空引用类型(Nullable reference types)
从此,引用类型将会区分是否可分,可以从根源上解决 NullReferenceException。但是由于这个特性会打破兼容性,因此没有当作 error 来对待,而是使用 warning 折衷,而且开发人员需要手动 opt-in 才可以使用该特性(可以在项目层级或者文件层级进行设定)。
例如:
string s = null; // 产生警告: 对不可空引用类型赋值 null string? s = null; // Ok void M(string? s) { Console.WriteLine(s.Length); // 产生警告:可能为 null if (s != null) { Console.WriteLine(s.Length); // Ok } }12345678910
至此,妈妈再也不用担心我的程序到处报 NullReferenceException 啦!
异步流(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; } }12345678910111213
范围和下标类型(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"123
除此之外,还引入了 “..” 操作符用来表示范围(注意是左闭右开区间)。
var slice = a[i1..i2]; // { 3, 4, 5 }
关于这个下标从 0 开始,倒数从 1 开始,范围左闭右开,笔者刚开始觉得很奇怪,但是发现 Python 等语言早已经做了这样的实践,并且效果不错。因此这次微软也采用了这种方式设计了 C# 8.0 的这个语法。
接口的默认实现方法(Default implementations of interface members)
从此接口中可以包含实现了:
interface ILogger { void Log(LogLevel level, string message); void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); // 这是一个默认实现重载 } class ConsoleLogger : ILogger { public void Log(LogLevel level, string message) { ... } // Log(Exception) 会得到执行的默认实现 }12345678910
在上面的例子中,Log(Exception)
将会得到执行的默认实现。
模式匹配表达式和递归模式语句(Switch expressions and recursive patterns)
现在可以这么写了(patterns 里可以包含 patterns)
IEnumerable<string> GetEnrollees() { foreach (var p in People) { if (p is Student { Graduated: false, Name: string name }) yield return name; } }123456
Student { Graduated: false, Name: string name }
检查 p 是否为 Graduated = false
且 Name
为 string
的 Student
,并且迭代返回 name
。
可以这样写之后是不是很爽?
更有:
var area = figure switch { Line _ => 0, Rectangle r => r.Width * r.Height, Circle c => c.Radius * 2.0 * Math.PI, _ => throw new UnknownFigureException(figure) };123456
典型的模式匹配语句,只不过没有用“match”关键字,而是沿用了
了“switch”关键字。
但是不得不说,一个字,爽!
目标类型推导(Target-typed new-expressions)
以前我们写下面这种变量/成员声明的时候,大概最简单的写法就是:
var points = new [] { new Point(1, 4), new Point(2, 6) }; private List<int> _myList = new List<int>();12
现在我们可以这么写啦:
Point[] ps = { new (1, 4), new (3,-2), new (9, 5) }; private List<int> _myList = new ();12
是不是更加的舒服了?
注意事项
- 以上的新特性需要 .NET Standard 2.1/.NET Core 3.0/.NET Framework 4.8 及以上来支持。
- 但是,由于接口的默认实现方法这个特性需要 CLR 的支持,而 .NET Framework 4.8 还没有来得及做出修改,因此此特性在 .NET Framework 4.8 中不可用,需要等待进一步的更新。
- C# 8.0 截至发文可以说已经定型了,正式发布还需要等一阵子。
一些想法
- 本次 C# 8.0 的更新,Record2估计是要被鸽了,有些小遗憾。
- C# 一直都在不断地完善和补充自己的语法体系,这和官方给出的 C# 发展目标相同,即:不断容纳各种优秀和现代的语法特性,追求多样化。相信这门优秀的语言未来会带给我们更多的惊喜。希望大家不要抱着老旧的看法对待这门语言,都 8012 年了我们也应该用全新的姿态去审视这门语言,去尝试一下新的语法对编码效率带来的大幅度提升。
- C# 的 IDE 除了 Visusl Studio 之外,还有 Visual Studio for Mac 以及跨平台的 Visual Studio Code、JetBrain 出品的 Rider 跨平台 C# IDE,极大程度的方便了开发者。
- 从近几年的发展来看,微软面对开源、生态建设、开发者、社区、跨平台等的重视程度不断上升,相信 .NET Core 的前景一定会更好。
参考文献
- https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0 ↩︎
- https://github.com/dotnet/csharplang/issues/39 ↩︎
原文链接:https://blog.csdn.net/hez2010/article/details/84036742
本文观点不代表 .Net中文网 立场,转载请联系原作者。
评论列表(1条)
[…] 该文章的最新版本已迁移至个人博客【比特飞】,单击链接 https://www.byteflyin… […]