新增功能内容: What's new in C# 8.0
本来想稍微翻译一下这份文档的,然而微软早就更新了中文文档 , 那这篇文章就很简单的理解下C#8中的新增内容吧。
C# 8.0 是在 .NetCore 3.x 才支持的,所以如果需要尝试新的语法的话需要手动安装 .NetCore SDK, 或者如果安装了Vs2019的话,更新VS2019.
Readonly 成员
可以将readonly
加在结构体的成员中。 这在类成员中我们已经经常使用,比如对于一些DI成员, 只在构造函数中初始化了只会就不会改变, 这样这种成员就适合定义成readonly
的:
//...
public class HomeService{
public readonly IHomeRepository _homeRepo;
public HomeService(IHomeRepository homeRepo){
_homeRepo = homeRepo;
}
}
现在,在结构体中的成员也可以定义乘readonly
(我都以为原来就可以), 比如定义一个举行:
//...
public struct Rect{
public double Width {get;set;}
public double Height {get;set;}
public readonly double Area => Width * Height;
}
请注意,
readonly
修饰符对于只读属性是必需的。 编译器会假设get
访问器可以修改状态;必须显式声明readonly
。
默认接口方法
现在可以将成员添加到接口,并为这些成员提供实现。
这是官方文档上给的一句话定义,简单的来说,就是现在可以在接口中定义属性,方法并且实现了:
interface IShape
{
public string Name { get; set; }
public string SayHi()
{
return $"this is {Name}";
}
}
class Rect : IShape
{
public string Name { get; set; }
}
class Triangle : IShape
{
public string Name { get; set; }
public string SayHi()
{
return $"这是 {Name}";
}
}
Switch改进:
Switch 表达式
对Switch语句
的一种简写形式, 尤其是对于简单的Switch语句
效果特别好:
var c = (Color)0;
// Switch 语句
string rgb1;
switch (c)
{
case Color.Red:
rgb1 = "f00";
break;
case Color.Green:
rgb1 = "0f0";
break;
case Color.Blue:
rgb1 = "00f";
break;
default:
throw new ArgumentException();
}
// Switch 表达式
string rgb2 = c switch
{
Color.Red => "f00",
Color.Green => "0f0",
Color.Blue => "00f",
_ => throw new ArgumentException()
};
// Assert.Equal(rgb1, rgb2);
属性模式
可以直接对对象
进行Switch
, 在Case
中判断对象的属性:
var t = new ColorInfo { Color = Color.Red };
_ = t switch
{
{ Color: Color.Red } => t.Chinese = "红色",
{ Color: Color.Green } => t.Chinese = "绿色",
{ Color: Color.Blue } => t.Chinese = "蓝色"
}
Assert.Equal("红色", t.Chinese);
元组模式
跟属性模式差不多, 可以将两个独立的值组成元组进行匹配:
public void TuplePatterns(Color a, Color b)
{
var res = (a, b) switch
{
(Color.Red, Color.Green) => Color.Yellow,
(Color.Red, Color.Blue) => Color.Purple,
(Color.Green, Color.Blue) => Color.Turq,
_ => throw new ArgumentException()
};
}
位置模式
这个还没有仔细看,大抵思路跟上面几种差不多,目的也是能够更灵活地进Switch-Case
匹配, 具体用法可以参考官方文档:
https://docs.microsoft.com/zh-cn/dotnet/csharp/tutorials/pattern-matching
Using 声明
这个是对原来Using 语句的进一步优化, 省略了大括号, 使用using
声明的变量,会在其作用域结束前释放:
static int TestUsingDeclare(IEnumerable<string> lines)
{
using var file = new System.IO.StreamWriter("file.txt");
// ....
// return something;
// file is disposed here
}
静态本地函数
对于本地函数, 如果它不访问其所在的函数内的任何变量, 可以将这个本地函数声明称静态函数:
int TestStaticLocalMethod()
{
int y = 5;
int x = 7;
return Add(x, y);
static int Add(int left, int right) => left + right;
}
可处置的 ref 结构(Disposable ref structs)
用
ref
修饰符声明的struct
可能无法实现任何接口,因此无法实现 IDisposable。 因此,要能够处理ref struct
,它必须有一个可访问的void Dispose()
方法。 此功能同样适用于readonly ref struct
声明
ref struct Point
{
public int X { get; set; }
public int Y { get; set; }
public void Dispose() { }
}
可空引用类型(文档)
示例:
在
nullable
注释上下文中,引用类型的任何变量都被视为不可为空引用类型 。 若要指示一个变量可能为 null,必须在类型名称后面附加?
,以将该变量声明为可为空引用类型
#nullable enable
string? a = null;
Assert.Equal(0, a?.Length??0);
a = "111";
Assert.Equal(3, a!.Length);
// 下面两句,编译器会报一个警告
string b = null;
Assert.Null(b);
#nullable disable
异步流
可以使用异步流, 创建异步流将返回IAsyncEnumerable
类型。
static async IAsyncEnumerable<int> GenerateSequence()
{
for (int i = 0; i < 20; i++)
{
await Task.Delay(100);
yield return i;
}
}
var res = 0;
await foreach (var number in GenerateSequence())
{
Assert.Equal(res++, number);
}
索引和范围
好东西啊! 这个类似于 python 中的切片语法,不过语法看起来稍微复杂一些, 不过怎么说有总比没有好啊。
^
表示从后面取..
表示取范围
var arr = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Assert.Equal(9, arr[^1]);
// 单独访问 arr[^0] 会报错
// 范围选择为左闭右开区间
Assert.Equal(1, arr[0..^0][0]);
Assert.Equal(9, arr[0..^0][^1]);
Assert.Equal(2, arr[1..^1][0]);
Assert.Equal(8, arr[1..^1][^1]);
注: 支持的类型:
- 支持索引和范围: Array, String, Span
, ReadOnlySpan - 仅支持索引: List
Null 合并赋值: ??=
a ??= b <==> a = a != null ? a : b
非托管构造类型
如果某个类型是以下类型之一,则它是非托管类型 :
sbyte
、byte
、short
、ushort
、int
、uint
、long
、ulong
、char
、float
、double
、decimal
或bool
- 任何枚举类型
- 任何指针类型
- 任何用户定义的 struct 类型,只包含非托管类型的字段,并且在 C# 7.3 及更早版本中,不是构造类型(包含至少一个类型参数的类型)
从 C# 7.3 开始,可使用
unmanaged
约束指定:类型参数为“非指针、不可为 null 的非托管类型”。从 C# 8.0 开始,仅包含非托管类型的字段的构造结构类型也是非托管类型
嵌套表达式中的 Stackalloc
从 C# 8.0 开始,如果 stackalloc 表达式的结果为 System.Span 或 System.ReadOnlySpan 类型,则可以在其他表达式中使用
stackalloc
表达式:
unsafe
{
// 默认会返回 int* 类型
var numbers = stackalloc[] { 1, 2, 3, 4, 5, 6 };
}
Span<int> numbers2 = stackalloc[] { 1, 2, 3, 4, 5, 6 };
内插逐字字符串的增强功能
即字符串声明时$@"dd{ff}"
和 @$"dd{ff}"
都是可以的, 这个如果之前已经习惯了 $@
的就继续留用就好
本文测试代码: https://github.com/zhaokuohaha/SimpleTest/blob/master/SimpleTests/CSharp8Test.cs