首页 > 解决方案 > 是否有在 C# (Unity) 中编写异步委托的优雅解决方案

问题描述

我熟悉如何编写常见的委托并订阅它们,但是有没有办法让委托(订阅和取消订阅其他类)具有可等待的功能?

例如,使用这样的简单委托:

public delegate void SimpleDelegate();
public static SimpleDelegate OnDelegateInvoke;

然后我可以从另一个类订阅它,例如:

    public void SomeFunction(){};

    OnDelegateInvoke += SomeFunction();

我想要的行为是 OnDelegateInvoke 调用是可等待的,所以它在那里等待,直到所有订阅的函数完成:

     await OnDelegateInvoke?.Invoke();
     await DoSomethingThatNeedsAboveCompleted();

我尝试使用 Task 返回类型编写委托,但据我了解,这是行不通的,因为有多个函数返回多个任务,因此 await 只会等待第一个 Task 完成。

由于考虑到这一点,我也不确定这是否完全打破了代表为何有用的范式,因此也很感谢有关此问题的答案。

标签: c#async-await

解决方案


您可以通过代表返回 a 来执行此操作Task

public delegate Task SimpleAsyncDelegate();
public static SimpleAsyncDelegate OnAsyncDelegateInvoke;

要调用和等待所有添加的委托,我们一个接一个地调用它们并等待生成Task的 s:

var delegateTasks = OnAsyncDelegateInvoke.GetInvocationList()
    .Cast<SimpleAsyncDelegate>()
    .Select(del => del.Invoke());
await Task.WhenAll(delegateTasks);

有关等待和不等待的示例,请参见this fiddle


为了解决评论中的问题,我认为最接近等待所有代表的更通用方式的是一个类似于以下内容的辅助类:

public class DelegateHelper
{
    public static async Task WhenAllDelegates(Delegate del)
    {
        var delegateTasks = del.GetInvocationList()
            .Select(del => del.DynamicInvoke())
            .Where(obj => obj is Task)
            .Cast<Task>();
        await Task.WhenAll(delegateTasks);
    }
}

你可以像这样使用它:

await DelegateHelper.WhenAllDelegates(OnAsyncDelegateInvoke);

注意:对于我们需要使用的通用目的DynamicInvoke,根据这个答案,它的表现要差得多。这种性能是否真的是一个问题取决于用例,因此您可以测试它是否值得。


推荐阅读