Task<TResult>
(对于返回值的异步方法)。
void
(对于事件处理程序)。
任何具有可访问的
GetAwaiter
方法的类型。
GetAwaiter
方法返回的对象必须实现
System.Runtime.CompilerServices.ICriticalNotifyCompletion
接口。
IAsyncEnumerable<T>
(对于返回异步流的异步方法)。
有关异步方法的详细信息,请参阅
使用 Async 和 Await 的异步编程 (C#)
。
还存在特定于 Windows 工作负载的其他几种类型:
DispatcherOperation
,适用于仅限于 Windows 的异步操作。
IAsyncAction
,适用于 UWP 中不返回值的异步操作。
IAsyncActionWithProgress<TProgress>
,适用于 UWP 中只报告进程但不返回值的异步操作。
IAsyncOperation<TResult>
,适用于 UWP 中返回值的异步操作。
IAsyncOperationWithProgress<TResult,TProgress>
,适用于 UWP 中既报告进程又返回值的异步操作。
Task 返回类型
不包含
return
语句的异步方法或包含不返回操作数的
return
语句的异步方法通常具有返回类型
Task
。 如果此类方法同步运行,它们将返回
void
。 如果在异步方法中使用
Task
返回类型,调用方法可以使用
await
运算符暂停调用方的完成,直至被调用的异步方法结束。
下例中的
WaitAndApologizeAsync
方法不包含
return
语句,因此该方法会返回
Task
对象。 返回
Task
可等待
WaitAndApologizeAsync
。
Task
类型不包含
Result
属性,因为它不具有任何返回值。
public static async Task DisplayCurrentInfoAsync()
await WaitAndApologizeAsync();
Console.WriteLine($"Today is {DateTime.Now:D}");
Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}");
Console.WriteLine("The current temperature is 76 degrees.");
static async Task WaitAndApologizeAsync()
await Task.Delay(2000);
Console.WriteLine("Sorry for the delay...\n");
// Example output:
// Sorry for the delay...
// Today is Monday, August 17, 2020
// The current time is 12:59:24.2183304
// The current temperature is 76 degrees.
通过使用 await 语句而不是 await 表达式等待
WaitAndApologizeAsync
,类似于返回 void 的同步方法的调用语句。 Await 运算符的应用程序在这种情况下不生成值。 当
await
的右操作数是
Task<TResult>
时,
await
表达式生成的结果为
T
。 当
await
的右操作数是
Task
时,
await
及其操作数是一个语句。
可从 await 运算符的应用程序中分离对
WaitAndApologizeAsync
的调用,如以下代码所示。 但是,请记住,
Task
没有
Result
属性,并且当 await 运算符应用于
Task
时不产生值。
以下代码将调用
WaitAndApologizeAsync
方法和等待此方法返回的任务分离。
Task waitAndApologizeTask = WaitAndApologizeAsync();
string output =
$"Today is {DateTime.Now:D}\n" +
$"The current time is {DateTime.Now.TimeOfDay:t}\n" +
"The current temperature is 76 degrees.\n";
await waitAndApologizeTask;
Console.WriteLine(output);
Task<TResult> 返回类型
Task<TResult>
返回类型用于某种异步方法,此异步方法包含
return
语句,其中操作数是
TResult
。
在下面的示例中,
GetLeisureHoursAsync
方法包含返回整数的
return
语句。 该方法声明必须指定
Task<int>
的返回类型。
FromResult
异步方法是返回
DayOfWeek
的操作的占位符。
public static async Task ShowTodaysInfoAsync()
string message =
$"Today is {DateTime.Today:D}\n" +
"Today's hours of leisure: " +
$"{await GetLeisureHoursAsync()}";
Console.WriteLine(message);
static async Task<int> GetLeisureHoursAsync()
DayOfWeek today = await Task.FromResult(DateTime.Now.DayOfWeek);
int leisureHours =
today is DayOfWeek.Saturday || today is DayOfWeek.Sunday
? 16 : 5;
return leisureHours;
// Example output:
// Today is Wednesday, May 24, 2017
// Today's hours of leisure: 5
在
ShowTodaysInfo
方法中从 await 表达式内调用
GetLeisureHoursAsync
时,await 表达式检索存储在由
GetLeisureHours
方法返回的任务中的整数值(
leisureHours
的值)。 有关 await 表达式的详细信息,请参阅
await
。
通过从应用程序
await
中分离对
GetLeisureHoursAsync
的调用,可以更好地理解
await
如何从
Task<T>
检索结果,如以下代码所示。 对非立即等待的方法
GetLeisureHoursAsync
的调用返回
Task<int>
,正如你从方法声明预料的一样。 该任务指派给示例中的
getLeisureHoursTask
变量。 因为
getLeisureHoursTask
是
Task<TResult>
,所以它包含类型
TResult
的
Result
属性。 在这种情况下,
TResult
表示整数类型。
await
应用于
getLeisureHoursTask
,await 表达式的计算结果为
getLeisureHoursTask
的
Result
属性内容。 此值分配给
ret
变量。
Result
属性为阻止属性。 如果你在其任务完成之前尝试访问它,当前处于活动状态的线程将被阻止,直到任务完成且值为可用。 在大多数情况下,应通过使用
await
访问此值,而不是直接访问属性。
上一示例通过检索
Result
属性的值来阻止主线程,从而使
Main
方法可在应用程序结束之前将
message
输出到控制台。
var getLeisureHoursTask = GetLeisureHoursAsync();
string message =
$"Today is {DateTime.Today:D}\n" +
"Today's hours of leisure: " +
$"{await getLeisureHoursTask}";
Console.WriteLine(message);
Void 返回类型
在异步事件处理程序中使用
void
返回类型,这需要
void
返回类型。 对于事件处理程序以外的不返回值的方法,应返回
Task
,因为无法等待返回
void
的异步方法。 此类方法的任何调用方都必须继续完成,而无需等待调用的异步方法完成。 调用方必须独立于异步方法生成的任何值或异常。
Void 返回异步方法的调用方无法捕获从该方法引发的异常。 此类未经处理异常有可能导致应用程序失败。 如果返回
Task
或
Task<TResult>
的方法引发异常,则该异常存储在返回的任务中。 等待任务时,将重新引发异常。 请确保可以产生异常的任何异步方法都具有返回类型
Task
或
Task<TResult>
,并确保会等待对方法的调用。
以下示例演示异步事件处理程序的行为。 在本示例代码中,异步事件处理程序必须在完成时通知主线程。 然后,主线程可在退出程序之前等待异步事件处理程序完成。
public class NaiveButton
public event EventHandler? Clicked;
public void Click()
Console.WriteLine("Somebody has clicked a button. Let's raise the event...");
Clicked?.Invoke(this, EventArgs.Empty);
Console.WriteLine("All listeners are notified.");
public class AsyncVoidExample
static readonly TaskCompletionSource<bool> s_tcs = new TaskCompletionSource<bool>();
public static async Task MultipleEventHandlersAsync()
Task<bool> secondHandlerFinished = s_tcs.Task;
var button = new NaiveButton();
button.Clicked += OnButtonClicked1;
button.Clicked += OnButtonClicked2Async;
button.Clicked += OnButtonClicked3;
Console.WriteLine("Before button.Click() is called...");
button.Click();
Console.WriteLine("After button.Click() is called...");
await secondHandlerFinished;
private static void OnButtonClicked1(object? sender, EventArgs e)
Console.WriteLine(" Handler 1 is starting...");
Task.Delay(100).Wait();
Console.WriteLine(" Handler 1 is done.");
private static async void OnButtonClicked2Async(object? sender, EventArgs e)
Console.WriteLine(" Handler 2 is starting...");
Task.Delay(100).Wait();
Console.WriteLine(" Handler 2 is about to go async...");
await Task.Delay(500);
Console.WriteLine(" Handler 2 is done.");
s_tcs.SetResult(true);
private static void OnButtonClicked3(object? sender, EventArgs e)
Console.WriteLine(" Handler 3 is starting...");
Task.Delay(100).Wait();
Console.WriteLine(" Handler 3 is done.");
// Example output:
// Before button.Click() is called...
// Somebody has clicked a button. Let's raise the event...
// Handler 1 is starting...
// Handler 1 is done.
// Handler 2 is starting...
// Handler 2 is about to go async...
// Handler 3 is starting...
// Handler 3 is done.
// All listeners are notified.
// After button.Click() is called...
// Handler 2 is done.
通用的异步返回类型和 ValueTask<TResult>
异步方法可以返回具有返回 awaiter 类型实例的可访问
GetAwaiter
方法的所有类型。 此外,
GetAwaiter
方法返回的类型必须具有
System.Runtime.CompilerServices.AsyncMethodBuilderAttribute
特性。 可以通过有关
编译器读取的属性
的文章或
任务类型生成器模式
的 C# 规范,了解详细信息。
此功能与
awaitable 表达式
相辅相成,后者描述
await
操作数的要求。 编译器可以使用通用异步返回类型生成返回不同类型的
async
方法。 通用异步返回类型通过 .NET 库实现性能改进。
Task
和
Task<TResult>
是引用类型,因此,性能关键路径中的内存分配会对性能产生负面影响,尤其当分配出现在紧凑循环中时。 支持通用返回类型意味着可返回轻量值类型(而不是引用类型),从而避免额外的内存分配。
.NET 提供
System.Threading.Tasks.ValueTask<TResult>
结构作为返回任务的通用值的轻量实现。 如下示例使用
ValueTask<TResult>
结构检索两个骰子的值。
class Program
static readonly Random s_rnd = new Random();
static async Task Main() =>
Console.WriteLine($"You rolled {await GetDiceRollAsync()}");
static async ValueTask<int> GetDiceRollAsync()
Console.WriteLine("Shaking dice...");
int roll1 = await RollAsync();
int roll2 = await RollAsync();
return roll1 + roll2;
static async ValueTask<int> RollAsync()
await Task.Delay(500);
int diceRoll = s_rnd.Next(1, 7);
return diceRoll;
// Example output:
// Shaking dice...
// You rolled 8
编写通用异步返回类型是一种高级方案,旨在用于专门的环境。 请考虑改用
Task
、
Task<T>
和
ValueTask<T>
类型,它们适用于大多数的异步代码方案。
在 C# 10 及更高版本中,可以将
AsyncMethodBuilder
属性应用于异步方法(而不是异步返回类型声明),用于替代该类型的生成器。 通常会应用此属性来利用 .NET 运行时中提供的另一种生成器。
使用 IAsyncEnumerable<T> 的异步流
异步方法可能返回异步流,由
IAsyncEnumerable<T>
表示。 异步流提供了一种方法,来枚举在具有重复异步调用的块中生成元素时从流中读取的项。 以下示例显示生成异步流的异步方法:
static async IAsyncEnumerable<string> ReadWordsFromStreamAsync()
string data =
@"This is a line of text.
Here is the second line of text.
And there is one more for good measure.
Wait, that was the penultimate line.";
using var readStream = new StringReader(data);
string? line = await readStream.ReadLineAsync();
while (line != null)
foreach (string word in line.Split(' ', StringSplitOptions.RemoveEmptyEntries))
yield return word;
line = await readStream.ReadLineAsync();
前面的示例异步读取字符串中的行。 读取每一行后,代码将枚举字符串中的每个单词。 调用方将使用
await foreach
语句枚举每个单词。 当需要从源字符串异步读取下一行时,该方法将等待。
FromResult
在异步任务完成时对其进行处理
使用 Async 和 Await 的异步编程 (C#)
async
await