在C#8之前,可以使用yield return實現叠代器,也可以用await書寫異步函數。但無法兩者結合,實現一個可以等待的叠代器。C#8引入了異步流解決了這個問題。
異步流基于以下兩個接口。
public interface IAsyncEnumerable<out T>
{
IAsyncEnumerator<T> GetAsyncEnumerator(...);
}
public interface IAsyncEnumerator<out T>
: IAsyncDisposable
{
T Current { get; }
ValueTask<bool> MoveNextAsync();
}
ValueTask<T>結構體封裝了Task<T>,其行為和Task<T>相似。在任務對象同步完成的情況下具有更好的執行性能。
結合使用叠代器和異步方法的規則來書寫方法就可以創建一個異步流。即該方法應同時具備yield return和await,并且返回值為IAsyncEnumerable<T>:
async IAsyncEnumerable<int> RangeAsync (
int start, int count, int delay)
{
for (int i = start; i < start count; i )
{
await Task.Delay (delay);
yield return i;
}
}
相應地,使用await foreach語句就可以消費異步流:
await foreach (var number in RangeAsync (0, 10, 500))
{
Console.WriteLine (number);
}
以上例子,數據持續以每0.5秒一個的速度到達。 而如果像以下例子中使用Task<IEnumerable<T>>,則隻有所有數據都準備好時才會最終返回:
static async Task<IEnumerable<int>>
RangeTaskAsync(int start, int count, int delay)
{
List<int> data = new List<int>();
for (int i = start; i < start count; i )
{
await Task.Delay (delay);
data.Add (i);
}
return data;
}
消費上例中的結果隻需使用foreach語句即可:
foreach (var data in await RangeTaskAsync(0, 10, 500))
{
Console.WriteLine (data);
}
System.Linq.Async Nuget包包含了對IAsyncEnumerable<T>查詢的LINQ查詢運算符。這樣就可以像查詢IEnumerable<T>那樣書寫查詢了。 例如,可以對前面RangeAsync方法書寫LINQ查詢:
IAsyncEnumerable<int> query =
from i in RangeAsync (0, 10, 500)
where i % 2 == 0
select i * 10;
await foreach (var number in query)
{
Console.WriteLine (number);
}
ASP.NET Core控制器的方法目前支持返回IAsyncEnumerable<T>對象了,此類方法必須标記async:
[HttpGet]
public async IAsyncEnumerable<string> Get()
{
using var dbContext = new DbContext();
await foreach (var title in dbContext.Books
.Select(b=>b.Title).AsAsyncEnumerable())
{
yield return title;
}
}
更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!