windows C#-异步返回类型(下)

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 特性。

此功能与 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 语句枚举每个单词。 当需要从源字符串异步读取下一行时,该方法将等待。

相关推荐
geovindu1 天前
CSharp: Observer Pattern
开发语言·后端·观察者模式·设计模式·c#·.netcore·行为模式
fangjianj1 天前
windows环境反弹shell
linux·windows·web安全
有味道的男人1 天前
得物详情 API|支持实时价格、成色、鉴别服务数据
windows·python·api
老王的笔记v1 天前
46期 Windows超级管理器 垃圾清理 预装卸载与系统优化工具箱
windows·系统架构
软件黑马王子1 天前
20.Resources资源加载:基本实现
开发语言·前端框架·c#
张人玉1 天前
基于 C# / .NET 8 + Windows Forms + SQLite 的桌面银行业务演示系统——华夏示范银行管理系统
windows·sqlite·c#·.net
1314lay_10071 天前
C#调用Sql Server存储过程,并且使用传入表结构
数据库·经验分享·笔记·sqlserver·c#
慧都小妮子1 天前
.NET 报表控件选型对比:Stimulsoft 与 FastReport 怎么选
c#·.net·报表开发·数据可视化·报表控件·报表引擎选型
Mr-Apple1 天前
系统找不到指定的文件-CreateInstance/CreateVm/HCS/ERROR_FILE_NOT_FOUND
linux·windows·ubuntu·wsl
1314lay_10071 天前
C#调用Sql Server存储过程,有返回值的
数据库·sqlserver·c#