异步方法的返回类型(C#)
分类: .Net技术 ◆ 标签: #异步编程 #基础 #.Net ◆ 发布于: 2023-06-04 19:07:24

我们前面已经学习了关于.Net异步方法的返回有比较常见的几种类型:
- 返回
Task<T>
泛型类型,这里的T是指客户具体想返回的类型 - 返回
Task
非泛型类型,如果客户的异步方法无需返回值 - void, 只能用在事件异步方法中,用于定义方法无需返回值。
除了这几个返回值类型,我们还有如下几种:
- 从
C# 7.0
开始,可以返回任何支持方法GetAwaiter
的类型对象,返回对象值可以由方法GetAwaiter
来返回。 - 从
C# 8.0
开始,可以返回类型IAsyncEnumerable<T>
类型,主要是针对于异步返回返回一个异步的流(stream)
另外对于windows runtime
由如下几种返回类型:
DispatcherOperation
, 返回窗口的一个异步操作IAsyncAction
, 用于UWP
返回一个无值的action
IAsyncActionWithProgress<TProgress>
, 用于UWP
的异步动作,用于报告进度条。IAsyncOperation<TResult>
, 用于UWP
返回一个带返回值的一个异步操作。IAsyncOperationWithProgress<TResult,TProgress>
, 用于UWP
的一个异步操作的进度条,并且带一个返回值。
理解await
如何取得Task中的T值
我们先看一个代码:
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; }
在我们这段代码里,如果要从方法GetLeisureHoursAsync
中取得一个int
的值,我们可以使用分步的形式来调用它,例如:
var myTask = GetLeisureHoursAsync(); var myIntValue = await myTask;
当使用变量myTask
来调用方法的时候,并没有进行await
, 因此这个时候会返回一个Task<T>
的变量,需要注意的是,这个方法返回的结果会放在Task
类型的属性Result
中,这个Result
就是我们在定义的时候Task<TResult>
中使用的类型变量,同时需要注意的是,这个属性是一个同步的变量,因此在使用await
的时候,它会从属性Result
中取回这个变量,同时由于它是同步变量,因此它会被block
这里,同时会转换类型到用户定义的类型,这就解释了,为什么await
能从Task<T>
中返回T
。
泛型异步返回类型和ValueTask<T>
从C# 7.0
开始,异步方法的返回类型可以返回任何有可调用的方法GetAwaiter
的类型对象,即该对象必须由该类型的GetAwaiter
方法返回,同时该类必须使用属性System.Runtime.CompilerServices.AsyncMethodBuilderAttribute
修饰,那么这种类型就称为泛型异步返回类型。同时注意,泛型异步返回类型和Task<T>
不是一回事。 关于如何自定义该泛型异步返回类型,今后我们再介绍相关的内容,您可以参考文档:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-7.0/task-types。
为什么需要这个泛型异步返回类型的原因是因为Task
和Task<T>
毕竟都是引用类型,在某些场景下会特别影响性能,例如密封的内部循环等等,对于这一类型的泛型异步返回类型,就很适合,因为你可以根据场景实现自己的返回类型,而无需特别的使用Task<T>
, 同时.Net
已经帮我们定义了一个常用的泛型异步返回类型:ValueTask<T>
, 这样大家也可以直接使用这个现成的泛型异步返回参数也是可以的。
Task
, Task<T>
, ValueTask<T>
已经可以适用大多数场景了。
异步返回流
从C# 8.0
开始,异步方法支持返回类型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(); } }
关于异步返回的返回参数就介绍到这里了。