增加引用类型可为null:
//如果参数t是null,则会发出警告
public static void Test<T?>(T t){
}
模式匹配
1、swith 多条件匹配
## 1 元组模式
int a = 1;
int b = 10;
string c = (a, b) switch {
(1, 10) => "123",
_ => "default",
};
### 使用弃元模式
_ = (a, b) switch {
(1, 10) => "123",
_ => "default",
};
## 2 位置模式
public class PointA
{
public int X { get; }
public int Y { get; }
public int Z { get; }
public PointA(int x, int y) => (X, Y) = (x, y);
public PointA(int x, int y,int z) => (X, Y,Z) = (x, y,z);
public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
public void Deconstruct(out int x, out int y,out int z) => (x, y,z) = (X, Y,Z);
}
static string GetLocation(PointA point) => point switch
{
(0, 0) => "1",
(0, 0, 0) => "1",
var (x, y) when x > 0 && y > 0 =>"2",
var (_, _) => "3",// 当 x 或 y 不为null时
_ => "4"
};
Using 声明
## 先熟悉下使用 此处 C#7就能用了
using static System.Math;
using Tasks = System.Threading.Tasks;
using (FileStream fileStream = new FileStream("", FileMode.Open)) { }
## C#8
using FileStream fileStream2 = new FileStream("", FileMode.Open);
接口特性增加
--- 可以声明一个属性
--- 方法可以实现(不再是只定义)
public interface MyInterface
{
public int Age { get; set; }
void Method1(int age)
{
Console.WriteLine(age);
}
}
Readonly 成员
public readonly struct Coords
{
public Coords(double x, double y)
{
X = x;
Y = y;
}
public double X { get; set; }
public double Y { get; set; }
public override string ToString() => $"({X}, {Y})";
}
---- 结构体 局部只读
public struct Coords{
public double X { get; set; }
public double Y { get; set; }
private int counter;
public int Counter
{
readonly get => counter;
set => counter = value;
}
/*public Coords(double x, double y, int _counter)
{
X = x;
Y = y;
counter = _counter;
}*/
public override string ToString() => $"({X}, {Y})";
public readonly double Sum()
{
/*X = 200;
Y = 200;*/
return X + Y;
}
}
静态本地函数
总结
支持禁止从封闭范围捕获状态的本地函数。
详细设计
声明的本地函数 static 无法从封闭范围中捕获状态。 因此, this 局部函数中不提供封闭范围内的局部变量、参数和 static 。
static局部函数不能通过隐式或显式或引用来引用实例成员 this base 。
static局部函数可以引用 static 封闭范围内的成员。
static int z = 0;
int InnerMethod(){
int y = 5;
int x = 7;
return LocalStaticFunction(x, y);
//静态本地函数
static int LocalStaticFunction(int left, int right) {
return z + left + right;
}
}
异步流
C#8.0之前 支持迭代器方法和异步方法,但不支持同时作为迭代器和异步的方法。 我们应该通过允许 await 使用新的迭代器形式来纠正这种情况 async ,它将返回 IAsyncEnumerable<T> 或 IAsyncEnumerator<T> 而不是 IEnumerable<T> 或 IEnumerator<T> , IAsyncEnumerable<T> 在新的中使用 await foreach 。 IAsyncDisposable接口还用于启用异步清理。
新增接口: IAsyncDisposable 、IAsyncEnumerable/IAsyncEnumerator
通常情况下,任何时候清理资源所需的时间可能会很有用,例如关闭文件 (需要刷新) ,取消注册回调并提供一种方法来了解注销完成的时间等。
namespace System
{
public interface IAsyncDisposable
{
ValueTask DisposeAsync();
}
}
与一样 Dispose , DisposeAsync 多次调用都是可接受的,并且第一次调用之后的调用应被视为 nops(意味着后续调用都应该是 nop),返回同步完成的任务 (DisposeAsync 不需要线程安全的,并且无需支持) 的并发调用。
名称解释:nop不执行任何操作
namespace System.Collections.Generic
{
public interface IAsyncEnumerable<out T>
{
IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default);
}
public interface IAsyncEnumerator<out T> : IAsyncDisposable
{
ValueTask<bool> MoveNextAsync();
T Current { get; }
}
}
IAsyncEnumerator<T> enumerator = enumerable.GetAsyncEnumerator();
try
{
while (await enumerator.MoveNextAsync())
{
Use(enumerator.Current);
}
}
finally { await enumerator.DisposeAsync(); }
static async void Main(string[] args) {
IAsyncEnumerable<string> asyncEnumerable = AsyncEnumerableTest();
IAsyncEnumerator<string> asyncEnumerator = asyncEnumerable.GetAsyncEnumerator();
try{
while (await asyncEnumerator.MoveNextAsync()) {
Console.WriteLine(asyncEnumerator.Current);
}
}
finally { await asyncEnumerator.DisposeAsync(); }
}
## 异步迭代器,但await不能在这些迭代器的主体中使用
private static async IAsyncEnumerable<string> AsyncEnumerableTest()
{
await Task.Delay(100);
yield return "001";
yield return "002";
yield return "003";
yield return "004";
yield return "005";
}
- Task<bool> MoveNextAsync(); T current { get; }:使用 Task<bool> 将支持使用缓存的任务对象来表示同步、成功的 MoveNextAsync 调用,但异步完成仍需要分配。 通过返回 ValueTask<bool> ,我们允许枚举器对象自身实现 IValueTaskSource<bool> 并用作从返回的的后备 ValueTask<bool> MoveNextAsync ,这反过来允许大大降低开销。
- ValueTask<(bool, T)> MoveNextAsync();:更难使用,但这意味着它 T 不再是协变的。
foreach 引入异步
若要强制 foreach 改为仅考虑异步 api,请按 await 如下所示插入:
await foreach (var i in enumerable)
var enumerable = ...;
await foreach (T item in enumerable) { ... }
### 转换为的等效项:
var enumerable = ...;
var enumerator = enumerable.GetAsyncEnumerator();
try
{
while (await enumerator.MoveNextAsync())
{
T item = enumerator.Current;
...
}
}
finally
{
await enumerator.DisposeAsync(); // omitted, along with the try/finally, if the enumerator doesn't expose DisposeAsync
}
ConfigureAwait
此基于模式的编译允许 ConfigureAwait 通过扩展方法在所有等待中使用
await foreach (T item in enumerable.ConfigureAwait(false))
{
...
}
###这将基于我们还将添加到 .NET 的类型,可能会 System.Threading.Tasks.Extensions.dll:
LINQ增加异步方法
List<string> list = new List<string>();
list.Add("001");
list.Add("002");
list.Add("003");
list.Add("004");
list.Select(AsyncMethod);
async ValueTask<string> AsyncMethod(string item){
await Task.Yield();
return item + "-";
}
List<string> list = new List<string>();
list.Add("001");
list.Add("002");
list.Add("003");
list.Add("004");
list.Select(async (item) =>
{
await Task.Yield();
return item + "-";
});
public partial class Form1 : Form
{
CancellationTokenSource cancellationToken;
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
cancellationToken = new CancellationTokenSource();
CancellationToken token = cancellationToken.Token;
IAsyncEnumerable<int> enumerable = ReadIntAsync(token);
await Task.Delay(3000);
//设置要在循环访问时传递到 GetAsyncEnumerator(CancellationToken) 的 CancellationToken。
//enumerable.WithCancellation(token);
IAsyncEnumerator<int> enumerator = enumerable.GetAsyncEnumerator();
try
{
while (await enumerator.MoveNextAsync())
{
Debug.WriteLine(enumerator.Current);
}
}
catch (TaskCanceledException ex1)
{
Console.WriteLine(ex1.Message);
}
catch (OperationCanceledException ex2)
{
Console.WriteLine(ex2.Message);
}
finally
{
await enumerator.DisposeAsync();
}
}
private void button2_Click(object sender, EventArgs e)
{
cancellationToken = cancellationTokennew CancellationTokenSource();
cancellationToken.Cancel();
}
private async IAsyncEnumerable<int> ReadIntAsync([EnumeratorCancellation] CancellationToken _token)
{
try
{
for (int i = 0; i < 20; i++)
{
//取消任务及其子级
if (_token.IsCancellationRequested)
{
_token.ThrowIfCancellationRequested();
}
await Task.Delay(200, _token);
yield return i;
}
}
finally
{
Console.WriteLine("finally");
}
}
}
索引和范围
此功能与提供两个新的运算符,它们允许构造 System.Index 和 System.Range 对象,并使用它们在运行时索引/切片集合。
namespace System
{
public readonly struct Index
{
public Index(int value, bool fromEnd);
}
}
若要 System.Index 在数组元素访问中使用类型作为参数,需要以下成员:
int System.Index.GetOffset(int length);
int[] arr = new int[6] { 132, 67, 47, 58, 83,100};
Index index = new Index(8, true);
--- 偏移量
int offset = index.GetOffset(arr.Length);
的 .. 语法 System.Range 需要该 System.Range 类型,以及以下一个或多个成员:
namespace System
{
public readonly struct Range
{
public Range(System.Index start, System.Index end);
public static Range StartAt(System.Index start);
public static Range EndAt(System.Index end);
public static Range All { get; }
}
}
最后,对于 System.Range 要在数组元素访问表达式中使用的类型值,必须存在以下成员:
//System.Runtime.CompilerServices=> 意味着编译器一开始就可以编译
namespace System.Runtime.CompilerServices
{
public static class RuntimeHelpers
{
public static T[] GetSubArray<T>(T[] array, System.Range range);
}
}
语言会引入新的范围运算符 x..y 。 它是一个接受两个表达式的二元中缀运算符
System.Range operator ..(Index start = 0, Index end = ^0);
.. 将运算符称为 范围运算符
public Index(int value, bool fromEnd);
运算符 ^ 等价于 Index(int value, bool fromEnd)
示例:
int[] arr = new int[6] { 132, 67, 47, 58, 83,100};
//获取前三个元素 以某某作为截止
int[] vs1 = RuntimeHelpers.GetSubArray(arr, Range.EndAt(3));
//获取后三个元素 以某某作为开始
int[] vs2 = RuntimeHelpers.GetSubArray(arr, Range.StartAt(3));
Console.WriteLine(vs2);
Range range1 = Range.StartAt(2);//从第三个元素开始 startIndex<=x<lenth
Range range2 = new Range(2, 4);//从第三个元素开始 到第四个元素 startIndex<=x<endIndex
var array = new int[] { 1, 2, 3, 4, 5 };
var slice1 = array[range1];
var slice2 = array[range2];
fromEnd为true 从尾部计算下标 :offset = arr.length-index;
new Index(index, fromEnd: true).GetOffset(array.Length)
var array = new int[] { 1, 2, 3, 4, 5 };
var lastItem = array[^1]; // array[new Index(1, fromEnd: true)]
var slice1 = array[2..^3]; // array[new Range(2, new Index(3, fromEnd: true))]
var slice2 = array[..^3]; // array[Range.EndAt(new Index(3, fromEnd: true))]
var slice3 = array[2..]; // array[Range.StartAt(2)]
var slice4 = array[..]; // array[Range.All]
此外, System.Index 应从进行隐式转换 System.Int32 ,以避免对多维签名重载混合整数和索引的需求。
此新优先级组低于一元运算符 ,大于乘法算术运算符
内插逐字字符串的增强功能
内插字符串的结构
若要将字符串标识为内插字符串,可在该字符串前面加上 和 " 之间不能有任何空格。 若要连接多个内插字符串,请将 $ 特殊字符添加到每个字符串字面量。
具备内插表达式的项的结构如下所示:
{<interpolationExpression>[,<alignment>][:<formatString>]}
元素 | 描述 |
---|---|
interpolationExpression | 生成需要设置格式的结果的表达式。 null 的字符串表示形式为 String.Empty。 |
alignment | 常数表达式,如果值为正,则字符串表示形式为右对齐;如果值为负,则为左对齐。 数值为意味着字符串占位长度,如果小于表达式实际值 则以实际值长度计算。 |
formatString | 受表达式结果类型支持的格式字符串。 有关更多信息,请参阅格式字符串组件。 |
如时间格式:MM/dd/yy H:mm:ss zzz
GUID格式: N 或者 D (见下方示例) |
Console.WriteLine($"|{"Left000"}|{"000Right"}|");
Console.WriteLine($"|{"Left",-7}|{"Right",7}|");
---输出结果:--
|Left000|000Right|
|Left | Right|
-----
Guid guid = Guid.NewGuid();
Console.WriteLine($"{guid:N}");
小破站: https://www.bilibili.com/video/BV1E24y147Vc
抖茵: https://www.douyin.com/user/self?modal_id=7217801169001844007&showTab=post