Skip to main content

异步方法的返回类型(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

为什么需要这个泛型异步返回类型的原因是因为TaskTask<T>毕竟都是引用类型,在某些场景下会特别影响性能,例如密封的内部循环等等,对于这一类型的泛型异步返回类型,就很适合,因为你可以根据场景实现自己的返回类型,而无需特别的使用Task<T>, 同时.Net已经帮我们定义了一个常用的泛型异步返回类型:ValueTask<T>, 这样大家也可以直接使用这个现成的泛型异步返回参数也是可以的。

TaskTask<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();
    }
}

关于异步返回的返回参数就介绍到这里了。