Come si implementa un metodo delegato di azione asincrona?


132

Alcune informazioni di base.

Sto imparando lo stack dell'API Web e sto cercando di incapsulare tutti i dati sotto forma di un oggetto "Risultato" con parametri come Success ed ErrorCodes.

Metodi diversi, tuttavia, produrrebbero risultati e codici di errore diversi, ma l'oggetto risultato verrebbe generalmente istanziato allo stesso modo.

Per risparmiare un po 'di tempo e anche per saperne di più sulle funzionalità di asincronizzazione / attesa in C #, sto cercando di racchiudere tutti i corpi dei metodi delle mie azioni API Web in un delegato di azioni asincrono, ma sono stato preso in un attimo ...

Date le seguenti classi:

public class Result
{
    public bool Success { get; set; }
    public List<int> ErrorCodes{ get; set; }
}

public async Task<Result> GetResultAsync()
{
    return await DoSomethingAsync<Result>(result =>
    {
        // Do something here
        result.Success = true;

        if (SomethingIsTrue)
        {
            result.ErrorCodes.Add(404);
            result.Success = false;
        }
    }
}

Voglio scrivere un metodo che esegue un'azione su un oggetto risultato e restituirlo. Normalmente attraverso i metodi sincroni sarebbe

public T DoSomethingAsync<T>(Action<T> resultBody) where T : Result, new()
{
    T result = new T();
    resultBody(result);
    return result;
}

Ma come posso trasformare questo metodo in un metodo asincrono usando async / waitit?

Questo è quello che ho provato:

public async Task<T> DoSomethingAsync<T>(Action<T, Task> resultBody) 
    where T: Result, new()
{
    // But I don't know what do do from here.
    // What do I await?
}

1
Se stai newinserendo il T, perché il tuo metodo deve essere asincrono? AFAIK nel codice usando API asincrone, devi solo propagare l' asyncintuizione da altri metodi che usi.
millimoose,

Mi dispiace, sono ancora abbastanza nuovo in questo, cosa intendi quando dici che devi solo propagare, e cosa c'entra la novità con la T?
Albin Anke,

Penso di averlo capito, grazie mille mi hai dato qualcosa a cui pensare.
Albin Anke,

1
Perché stai anche provando a fare questo asincrono? Più spesso in situazioni non Web server fare un falso asincrono avvolgendo il codice sincrono nelle attività (come si sta tentando di fare) è più lento del semplice farlo in modo sincrono.
Scott Chamberlain,

1
@AlbinAnke Per "propagare" intendo che se si chiama un metodo .NET come Stream.ReadAsync()in un metodo, tale metodo dovrebbe essere asincrono e restituire un punto in Task<T>cui Tsarebbe stato restituito il metodo sincrono. L'idea è che in questo modo, ogni chiamante del tuo metodo può quindi "attendere in modo asincrono" (non so quale sia un buon termine per questo) affinché il sottostante Stream.ReadAsync()sia completato. Una metafora per questo che puoi usare è che l'asincrono è "infettivo" e si diffonde dall'I / O integrato di basso livello in un altro codice i cui risultati dipendono da quelli di detto I / O.
millimoose,

Risposte:


307

L' asyncequivalente di Action<T>è Func<T, Task>, quindi credo che questo sia quello che stai cercando:

public async Task<T> DoSomethingAsync<T>(Func<T, Task> resultBody)
    where T : Result, new()
{
  T result = new T();
  await resultBody(result);
  return result;
}

@Stephen Chiaramente sto cercando di implementare qualcosa di simile in un ligth Messenger MVVM, posso implementarlo allo stesso modo?
Juan Pablo Gomez,

@JuanPabloGomez: non ho familiarità con il loro tipo di messaggistica, ma non vedo perché non funzioni.
Stephen Cleary,

1
Questo è fantastico! Ho pensato che non sarebbe stato possibile realizzare un'azione asincrona e l'ho già considerato un difetto del linguaggio. Non ho pensato di usare un Func. Grazie.
Noel Widmer,

2
@DFSFOT: l'equivalente asincrono di un voidmetodo è un Taskmetodo di ritorno; quindi, l'equivalente asincrono di Actionè Func<Task>e l'equivalente asincrono di Action<T>è Func<T, Task>. Maggiori informazioni qui .
Stephen Cleary,

1
@DFSFOT: un metodo asincrono dovrebbe restituire Taskquando non ha un valore di ritorno. Se utilizza la asyncparola chiave, l' Taskistanza effettiva verrà creata da una macchina a stati, non direttamente dalla funzione.
Stephen Cleary,

-11

Quindi credo che il modo di implementare questo sia:

public Task<T> DoSomethingAsync<T>(Action<T> resultBody) where T : Result, new()
{
    return Task<T>.Factory.StartNew(() =>
    {
        T result = new T();
        resultBody(result);
        return result;
    });
}

7
Dovresti evitare Task.Run(e ancora di più StartNew) su ASP.NET.
Stephen Cleary,

Qual è il modo migliore per farlo?
Albin Anke,

Ho pubblicato una risposta e ho votato anche la risposta di @ svick. Sono entrambe buone risposte.
Stephen Cleary,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.