Wie implementieren Sie eine asynchrone Aktionsdelegatmethode?

132

Ein paar Hintergrundinformationen.

Ich lerne den Web-API-Stack und versuche, alle Daten in Form eines "Ergebnis" -Objekts mit Parametern wie Success und ErrorCodes zu kapseln.

Unterschiedliche Methoden würden jedoch unterschiedliche Ergebnisse und Fehlercodes erzeugen, aber das Ergebnisobjekt würde im Allgemeinen auf dieselbe Weise instanziiert.

Um Zeit zu sparen und mehr über Async / Wait-Funktionen in C # zu erfahren, versuche ich, alle Methodenkörper meiner Web-API-Aktionen in einen asynchronen Aktionsdelegierten zu packen, bin aber in einen kleinen Haken geraten ...

Angesichts der folgenden Klassen:

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

Ich möchte eine Methode schreiben, die eine Aktion für ein Ergebnisobjekt ausführt, und diese zurückgeben. Normalerweise wäre es durch synchrone Methoden

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

Aber wie kann ich diese Methode mit async / await in eine asynchrone Methode umwandeln?

Folgendes habe ich versucht:

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?
}
Albin Anke
quelle
1
Wenn Sie new-ing bis die T, warum Ihre Notwendigkeit Methode asynchron sein? AFAIK in Code mit asynchronen APIs müssen Sie nur die asyncNess von anderen Methoden, die Sie verwenden, weitergeben.
Millimoose
Tut mir leid, ich bin noch ziemlich neu in diesem Bereich. Was meinst du, wenn du sagst, dass du dich nur verbreiten musst, und was hat die Neuerung des T damit zu tun?
Albin Anke
Ich glaube, ich habe es herausgefunden. Danke, Millimoose, du hast mir etwas zum Nachdenken gegeben.
Albin Anke
1
Warum versuchst du überhaupt, dies asynchron zu machen? In Situationen, in denen Webserver nicht fälschlicherweise asynchron sind, indem synchroner Code in Aufgaben eingeschlossen wird (wie Sie es versuchen), ist dies häufiger langsamer als nur synchron.
Scott Chamberlain
1
@AlbinAnke Mit „propagieren“ Ich meine , dass , wenn Sie eine .NET - Methode wie der Aufruf Stream.ReadAsync()in einem Verfahren, das Verfahren selbst asynchron sein sollte, und kehrt ein , Task<T>wo Tist das, was man die Methode synchron waren zurückgekehrt ist. Die Idee ist, dass auf diese Weise jeder Aufrufer Ihrer Methode "asynchron warten" kann (ich weiß nicht, was ein guter Begriff dafür ist), bis der Basiswert Stream.ReadAsync()vollständig ist. Eine Metapher dafür, die Sie verwenden können, ist, dass Async "ansteckend" ist und sich von integrierten E / A auf niedriger Ebene in anderen Code ausbreitet, dessen Ergebnisse von denen dieser E / A abhängen.
Millimoose

Antworten:

307

Das asyncÄquivalent von Action<T>ist Func<T, Task>, also glaube ich, dass dies das ist, wonach Sie suchen:

public async Task<T> DoSomethingAsync<T>(Func<T, Task> resultBody)
    where T : Result, new()
{
  T result = new T();
  await resultBody(result);
  return result;
}
Stephen Cleary
quelle
@Stephen Klar, ich versuche, etwas Ähnliches in einen MVVM ligth Messenger zu implementieren. Kann ich auf die gleiche Weise implementieren?
Juan Pablo Gomez
@JuanPabloGomez: Ich bin nicht mit ihrer Art von Nachrichten vertraut, aber ich verstehe nicht, warum es nicht funktionieren würde.
Stephen Cleary
1
Das ist großartig! Ich dachte, es wäre nicht möglich, eine asynchrone Aktion durchzuführen, und betrachtete sie bereits als Sprachfehler. Ich habe nicht daran gedacht, einen Func zu verwenden. Vielen Dank.
Noel Widmer
2
@DFSFOT: Das asynchrone Äquivalent einer voidMethode ist eine TaskRückgabemethode. Somit ist das asynchrone Äquivalent von Actionis Func<Task>und das asynchrone Äquivalent von Action<T>is Func<T, Task>. Mehr Infos hier .
Stephen Cleary
1
@DFSFOT: Eine asynchrone Methode sollte zurückgegeben werden, Taskwenn sie keinen Rückgabewert hat. Wenn das asyncSchlüsselwort verwendet wird, wird die tatsächliche TaskInstanz von einer Zustandsmaschine erstellt, nicht von der Funktion direkt.
Stephen Cleary
-11

Ich glaube, der Weg, dies umzusetzen, ist:

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;
    });
}
Albin Anke
quelle
7
Sie sollten ASP.NET vermeiden Task.Run(und noch mehr StartNew).
Stephen Cleary
Was ist ein besserer Weg, dies zu tun?
Albin Anke
Ich habe eine Antwort gepostet und auch die Antwort von @ svick positiv bewertet. Sie sind beide gute Antworten.
Stephen Cleary