--- 方法可以实现(不再只是定义)
public interface MyInterface
{
public int Age { get; set; }
void Method1(int age)
{
Console.WriteLine(age);
}
}
成员
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 override string ToString() => $"({X}, {Y})";
public readonly double Sum()
{
return X + Y;
}
}
静态局部函数总结
支持禁止从封闭范围捕获状态的本机函数。
详细设计
声明的局部函数无法从封闭范围捕获状态。 因此,封闭范围内的局部变量、参数和 this 在局部函数中是不可用的。
本地函数不能隐式或显式或通过引用引用实例成员 this base。
局部函数可以引用封闭范围内的成员。
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 来纠正这个问题,它将返回一个 or 而不是 or ,在 new 中使用 await 。 接口还用于启用异步清理。
新界面:、/
通常,任何清理资源的时间都是有用的,例如关闭文件(需要刷新)、注销回调以及提供一种了解注销何时完成的方法等。
namespace System
{
public interface IAsyncDisposable
{
ValueTask DisposeAsync();
}
}
和with一样,多次调用是可以接受的,第一次调用之后的调用应该被当作nops(意思是后面的调用都应该是nops),返回同步完成的任务(不需要是线程安全的,也不需要支持并发调用).
名称解释:nop 什么都不做
namespace System.Collections.Generic
{
public interface IAsyncEnumerable
{
IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default);
}
public interface IAsyncEnumerator : IAsyncDisposable
{
ValueTask MoveNextAsync();
T Current { get; }
}
}
IAsyncEnumerator enumerator = enumerable.GetAsyncEnumerator();
try
{
while (await enumerator.MoveNextAsync())
{
Use(enumerator.Current);
}
}
finally { await enumerator.DisposeAsync(); }
static async void Main(string[] args) {
IAsyncEnumerable asyncEnumerable = AsyncEnumerableTest();
IAsyncEnumerator asyncEnumerator = asyncEnumerable.GetAsyncEnumerator();
try{
while (await asyncEnumerator.MoveNextAsync()) {
Console.WriteLine(asyncEnumerator.Current);
}
}
finally { await asyncEnumerator.DisposeAsync(); }
}
## 异步迭代器,但await不能在这些迭代器的主体中使用
private static async IAsyncEnumerable AsyncEnumerableTest()
{
await Task.Delay(100);
yield return "001";
yield return "002";
yield return "003";
yield return "004";
yield return "005";
}
引入异步
要强制只考虑异步 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
}
这种基于模式的编译允许在所有等待中使用扩展方法
await foreach (T item in enumerable.ConfigureAwait(false))
{
...
}
###这将基于我们还将添加到 .NET 的类型,可能会 System.Threading.Tasks.Extensions.dll:
LINQ 添加异步方法
List list = new List();
list.Add("001");
list.Add("002");
list.Add("003");
list.Add("004");
list.Select(AsyncMethod);
async ValueTask AsyncMethod(string item){
await Task.Yield();
return item + "-";
}
List list = new List();
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 enumerable = ReadIntAsync(token);
await Task.Delay(3000);
//设置要在循环访问时传递到 GetAsyncEnumerator(CancellationToken) 的 CancellationToken。
//enumerable.WithCancellation(token);
IAsyncEnumerator 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 = cancellationToken??new CancellationTokenSource();
cancellationToken.Cancel();
}
private async IAsyncEnumerable 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");
}
}
}
索引和范围
此功能提供了两个新的运算符,允许构造 .Index 和 .Range 对象并在运行时使用它们对集合进行索引/切片。
namespace System
{
public readonly struct Index
{
public Index(int value, bool fromEnd);
}
}
要将 .Index 类型用作数组元素访问中的参数,需要以下成员:
int System.Index.GetOffset(int length);
int[] arr = new int[6] { 132, 67, 47, 58, 83, 100 };
索引 index = new Index(8, true);
- - 抵消
int = index.(arr.);
.Range 的 .. 语法需要 .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; }
}
}
最后,对于要在数组元素访问表达式中使用的 .Range 类型的值,必须存在以下成员:
//System.Runtime.CompilerServices=> 意味着编译器一开始就可以编译
namespace System.Runtime.CompilerServices
{
public static class RuntimeHelpers
{
public static T[] GetSubArray(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 )
例子:
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
从尾部开始计算下标为真:= arr.-index;
新索引(索引,:真)。(数组。)
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]
此外,.Index 应该从 .Int32 隐式转换,以避免需要混合整数和索引的多维签名重载。
这个新的优先级组低于一元运算符并大于乘法算术运算符
插入逐字字符串的增强功能 插入字符串的结构
要将字符串标识为内插字符串,请在字符串前面加上
和 " 之间不能有任何空格。要连接多个内插字符串,请将 $ 特殊字符添加到每个字符串文字。
带有插值表达式的项的结构如下:
{pression>[,][:]}
元素描述
离子
产生需要格式化的结果的表达式。 null 的字符串表示形式是 .Empty。
一个常量表达式,如果值为正,则其字符串表示形式右对齐,如果值为负,则其字符串表示形式左对齐。 该值为字符串的长度,如果小于表达式的实际值,则按实际值长度计算。
表达式结果类型支持的格式字符串。 有关详细信息,请参阅格式字符串组件。
比如时间格式: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}");
小破站:
颤音: