Esiste una soluzione elegante per scrivere delegati asincroni in C# (Unity)

Esiste una soluzione elegante per scrivere delegati asincroni in C# (Unity)

Soluzione per Esiste una soluzione elegante per la scrittura di delegati asincroni in C# (Unity)
di seguito:

Conosco come scrivere delegati comuni e iscrivermi a loro, ma c'è un modo per avere l'utilità dei delegati (iscriversi e annullare l'iscrizione ad altre classi) con la funzionalità attesa?

Ad esempio, con un semplice delegato come questo:

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

Posso quindi iscrivermi ad esso da un'altra classe come:

    public void SomeFunction(){};

    OnDelegateInvoke += SomeFunction();

Il comportamento che desidero è che la chiamata OnDelegateInvoke sia in attesa, quindi attende lì fino al completamento di tutte le funzioni sottoscritte:

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

Ho provato a scrivere i delegati con un tipo restituito di attività, ma a quanto ho capito non funzionerebbe poiché ci sono più funzioni che restituiscono più attività, quindi l'attesa aspetterebbe solo il completamento della prima attività.

Dato che ci penso, non sono nemmeno sicuro che questo rompa completamente il paradigma del perché i delegati siano utili, quindi anche le risposte su questo sono apprezzate.

Puoi farlo con i delegati che restituiscono un Task :

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

Per invocare e attendere tutti i delegati aggiunti, li invochiamo uno per uno e attendiamo il Task prodotto s:

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

Per un esempio con e senza in attesa, guarda questo violino.

Per rispondere alla domanda nei commenti, penso che la cosa più vicina a un modo più generico di attendere tutti i delegati sia una classe helper simile alla seguente:

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

Puoi usarlo in questo modo:

await DelegateHelper.WhenAllDelegates(OnAsyncDelegateInvoke);

NOTA: Per lo scopo generico dobbiamo usare DynamicInvoke , che in base a questa risposta ha prestazioni significativamente peggiori. Se queste prestazioni sono davvero un problema dipende dal caso d'uso, quindi puoi provare per vedere se ne vale la pena.