2018年11月12日微软在 MSDN 博客的 Building C# 8.01 一文发表了新的 C# 8.0 即将发布的特性,现在让我们来看一下。
从此,引用类型将会区分是否可分,可以从根源上解决 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
}
}
至此,妈妈再也不用担心我的程序到处报 NullReferenceException 啦!
考虑到大部分 Api 以及函数实现都有了对应的 async
版本,而 IEnumerable
和 IEnumerator
还不能方便的使用 async
/await
就显得很麻烦了。
但是,现在引入了异步流,这些问题得到了解决。
我们通过新的 IAsyncEnumerable
和 IAsyncEnumerator
来实现这一点。同时,由于之前 foreach
是基于IEnumerable
和 IEnumerator
实现的,因此引入了新的语法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;
}
}
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 开始,范围左闭右开,笔者刚开始觉得很奇怪,但是发现 Python 等语言早已经做了这样的实践,并且效果不错。因此这次微软也采用了这种方式设计了 C# 8.0 的这个语法。
从此接口中可以包含实现了:
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) 会得到执行的默认实现
}
在上面的例子中,Log(Exception)
将会得到执行的默认实现。
现在可以这么写了(patterns 里可以包含 patterns)
IEnumerable<string> GetEnrollees()
{
foreach (var p in People)
{
if (p is Student { Graduated: false, Name: string name }) yield return name;
}
}
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)
};
典型的模式匹配语句,只不过没有用“match”关键字,而是沿用了
了“switch”关键字。
但是不得不说,一个字,爽!
以前我们写下面这种变量/成员声明的时候,大概最简单的写法就是:
var points = new [] { new Point(1, 4), new Point(2, 6) };
private List<int> _myList = new List<int>();
现在我们可以这么写啦:
Point[] ps = { new (1, 4), new (3,-2), new (9, 5) };
private List<int> _myList = new ();
是不是更加的舒服了?
https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0 ↩︎
https://github.com/dotnet/csharplang/issues/39 ↩︎