Asynchrone Rückgabetypen (C#)Async return types (C#)

Asynchrone Methoden können folgende Rückgabetypen haben:Async methods can have the following return types:

  • Task für eine asynchrone Methode, die einen Vorgang ausführt, aber keinen Wert zurückgibt.Task, for an async method that performs an operation but returns no value.
  • Task<TResult> für eine asynchrone Methode, die einen Wert zurückgibt.Task<TResult>, for an async method that returns a value.
  • void für einen Ereignishandler.void, for an event handler.
  • Ab C# 7.0: jeder Typ, der über eine zugängliche GetAwaiter-Methode verfügt.Starting with C# 7.0, any type that has an accessible GetAwaiter method. Das von der GetAwaiter-Methode zurückgegebene Objekt muss die System.Runtime.CompilerServices.ICriticalNotifyCompletion-Schnittstelle implementieren.The object returned by the GetAwaiter method must implement the System.Runtime.CompilerServices.ICriticalNotifyCompletion interface.
  • Ab C# 8.0 IAsyncEnumerable<T> für eine asynchrone Methode, die einen asynchronen Datenstrom zurückgibt.Starting with C# 8.0, IAsyncEnumerable<T>, for an async method that returns an async stream.

Weitere Informationen über async-Methoden finden Sie unter Asynchrone Programmierung mit async und await (C#).For more information about async methods, see Asynchronous programming with async and await (C#).

Es gibt auch einige weitere Typen, die spezifisch für Windows-Workloads gelten:Several other types also exist that are specific to Windows workloads:

Task-RückgabetypTask return type

Asynchrone Methoden, die keine return-Anweisung enthalten oder eine return-Anweisung enthalten, die keinen Operanden zurückgibt, haben normalerweise einen Rückgabetyp von Task.Async methods that don't contain a return statement or that contain a return statement that doesn't return an operand usually have a return type of Task. Solche Methoden geben void zurück, wenn sie synchron ausgeführt werden.Such methods return void if they run synchronously. Wenn Sie einen Task-Rückgabetyp für eine asynchrone Methode verwenden, kann ein aufrufende Methode einen await-Operator verwenden, um den Abschluss des Aufrufers anzuhalten, bis die aufgerufene asynchrone Methode beendet ist.If you use a Task return type for an async method, a calling method can use an await operator to suspend the caller's completion until the called async method has finished.

Im folgenden Beispiel enthält die WaitAndApologizeAsync-Methode keine return-Anweisung, weshalb die Methode ein Task-Objekt zurückgibt.In the following example, the WaitAndApologizeAsync method doesn't contain a return statement, so the method returns a Task object. Durch Rückgabe von Task wird ermöglicht, dass WaitAndApologizeAsync erwartet wird.Returning a Task enables WaitAndApologizeAsync to be awaited. Der Typ Task enthält keine Result-Eigenschaft, da er nicht über einen Rückgabewert verfügt.The Task type doesn't include a Result property because it has no return value.

public static async Task DisplayCurrentInfoAsync()
{
    await WaitAndApologizeAsync();

    Console.WriteLine($"Today is {DateTime.Now:D}");
    Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}");
    Console.WriteLine("The current temperature is 76 degrees.");
}

static async Task WaitAndApologizeAsync()
{
    await Task.Delay(2000);

    Console.WriteLine("Sorry for the delay...\n");
}
// Example output:
//    Sorry for the delay...
//
// Today is Monday, August 17, 2020
// The current time is 12:59:24.2183304
// The current temperature is 76 degrees.

WaitAndApologizeAsync wird erwartet, indem eine „await“-Anweisung anstelle eines „await“-Ausdrucks verwendet wird, ähnlich der Aufrufanweisung einer Methode, die „void“ zurückgibt.WaitAndApologizeAsync is awaited by using an await statement instead of an await expression, similar to the calling statement for a synchronous void-returning method. Die Anwendung eines Erwartungsoperators erzeugt in diesem Fall keinen Wert.The application of an await operator in this case doesn't produce a value. Eine Erläuterung der Begriffe Anweisung und Ausdruck finden Sie in der folgenden Tabelle:To clarify the terms statement and expression, refer to the table below:

Art von „await“Await kind BeispielExample typeType
-Anweisung.Statement await SomeTaskMethodAsync() Task
AusdruckExpression T result = await SomeTaskMethodAsync<T>(); Task<TResult>

Sie können den WaitAndApologizeAsync-Aufruf eines await-Operators von der Anwendung trennen (dies wird im folgenden Code veranschaulicht).You can separate the call to WaitAndApologizeAsync from the application of an await operator, as the following code shows. Beachten Sie jedoch, dass Task über keine Result-Eigenschaft verfügt und dass kein Wert erzeugt wird, wenn ein Erwartungsoperator auf Task angewendet wird.However, remember that a Task doesn't have a Result property, and that no value is produced when an await operator is applied to a Task.

Der folgende Code trennt Aufrufe der Methode WaitAndApologizeAsync vom Erwarten der Aufgabe, die die Methode zurückgibt.The following code separates calling the WaitAndApologizeAsync method from awaiting the task that the method returns.

Task waitAndApologizeTask = WaitAndApologizeAsync();

string output =
    $"Today is {DateTime.Now:D}\n" +
    $"The current time is {DateTime.Now.TimeOfDay:t}\n" +
    "The current temperature is 76 degrees.\n";

await waitAndApologizeTask;
Console.WriteLine(output);

Task-Rückgabetyp <TResult>Task<TResult> return type

Der Rückgabetyp Task<TResult> wird für eine asynchrone Methode verwendet, die eine return-Anweisung enthält, in der der Operand TResult lautet.The Task<TResult> return type is used for an async method that contains a return statement in which the operand is TResult.

Im folgenden Beispiel enthält die GetLeisureHoursAsync-Methode eine return-Anweisung, die eine ganze Zahl zurückgibt.In the following example, the GetLeisureHoursAsync method contains a return statement that returns an integer. Aus diesem Grund muss die Methodendeklaration den Rückgabetyp Task<int> haben.Therefore, the method declaration must specify a return type of Task<int>. Die asynchrone Methode FromResult ist ein Platzhalter für einen Vorgang, der einen DayOfWeek-Wert zurückgibt.The FromResult async method is a placeholder for an operation that returns a DayOfWeek.

public static async Task ShowTodaysInfoAsync()
{
    string message =
        $"Today is {DateTime.Today:D}\n" +
        "Today's hours of leisure: " +
        $"{await GetLeisureHoursAsync()}";

    Console.WriteLine(message);
}

static async Task<int> GetLeisureHoursAsync()
{
    DayOfWeek today = await Task.FromResult(DateTime.Now.DayOfWeek);

    int leisureHours =
        today is DayOfWeek.Saturday || today is DayOfWeek.Sunday
        ? 16 : 5;

    return leisureHours;
}
// Example output:
//    Today is Wednesday, May 24, 2017
//    Today's hours of leisure: 5

Wenn GetLeisureHoursAsync aus einem „await“-Ausdruck in der Methode ShowTodaysInfo aufgerufen wird, ruft der „await“-Ausdruck den ganzzahligen Wert ab (der Wert von GetLeisureHours), der in der Aufgabe gespeichert wird, die von der Methode leisureHours zurückgegeben wird.When GetLeisureHoursAsync is called from within an await expression in the ShowTodaysInfo method, the await expression retrieves the integer value (the value of leisureHours) that's stored in the task returned by the GetLeisureHours method. Weitere Informationen zu await-Ausdrücken finden Sie unter await.For more information about await expressions, see await.

Wie await das Ergebnis aus einem Task<T> abruft, lässt sich besser erkennen, wenn Sie den Aufruf von GetLeisureHoursAsync von der Anwendung von await trennen, wie der folgende Code zeigt.You can better understand how await retrieves the result from a Task<T> by separating the call to GetLeisureHoursAsync from the application of await, as the following code shows. Ein Aufruf der GetLeisureHoursAsync-Methode, die nicht sofort eine Antwort erwartet, gibt ein Task<int> zurück, wie Sie es von der Deklaration der Methode erwarten.A call to method GetLeisureHoursAsync that isn't immediately awaited returns a Task<int>, as you would expect from the declaration of the method. Die Aufgabe wird im Beispiel der getLeisureHoursTask-Variablen zugewiesen.The task is assigned to the getLeisureHoursTask variable in the example. Da getLeisureHoursTask eine Task<TResult> ist, enthält es eine Result-Eigenschaft des Typs TResult.Because getLeisureHoursTask is a Task<TResult>, it contains a Result property of type TResult. In diesem Fall stellt TResult einen Integertyp dar.In this case, TResult represents an integer type. Wenn await auf getLeisureHoursTask angewendet wird, wertet der „await“-Ausdruck den Inhalt der Eigenschaft Result von getLeisureHoursTask aus.When await is applied to getLeisureHoursTask, the await expression evaluates to the contents of the Result property of getLeisureHoursTask. Der Wert wird der ret-Variablen zugewiesen.The value is assigned to the ret variable.

Wichtig

Die Result-Eigenschaft ist eine Blocking-Eigenschaft.The Result property is a blocking property. Wenn Sie darauf zuzugreifen versuchen, bevor seine Aufgabe beendet ist, wird der momentan aktive Thread blockiert, bis die Aufgabe abgeschlossen und der Wert verfügbar ist.If you try to access it before its task is finished, the thread that's currently active is blocked until the task completes and the value is available. In den meisten Fällen sollten Sie auf den Wert zugreifen, indem Sie await verwenden, anstatt direkt auf die Eigenschaft zuzugreifen.In most cases, you should access the value by using await instead of accessing the property directly.

Im vorherigen Beispiel wurde der Wert der Result-Eigenschaft abgerufen, um den Hauptthread zu blockieren, damit die Main-Methode message an die Konsole ausgeben konnte, bevor die Anwendung beendet wurde.The previous example retrieved the value of the Result property to block the main thread so that the Main method could print the message to the console before the application ended.

var getLeisureHoursTask = GetLeisureHoursAsync();

string message =
    $"Today is {DateTime.Today:D}\n" +
    "Today's hours of leisure: " +
    $"{await getLeisureHoursTask}";

Console.WriteLine(message);

Rückgabetyp „Void“Void return type

Der Rückgabetyp void wird in asynchronen Ereignishandlern verwendet, die den Rückgabetyp void erfordern.You use the void return type in asynchronous event handlers, which require a void return type. Für andere Methoden als Ereignishandler, die keinen Wert zurückgeben, sollten Sie stattdessen Task zurückgeben, da eine asynchrone Methode, die void zurückgibt, nicht erwartet werden kann.For methods other than event handlers that don't return a value, you should return a Task instead, because an async method that returns void can't be awaited. Jeder Aufrufer einer solchen Methode muss bis zum Abschluss ausgeführt werden, ohne darauf zu warten, dass die aufgerufene asynchrone Methode abgeschlossen wird.Any caller of such a method must continue to completion without waiting for the called async method to finish. Der Aufrufer muss von allen Werten oder Ausnahmen unabhängig sein, die von der asynchronen Methode generiert werden.The caller must be independent of any values or exceptions that the async method generates.

Der Aufrufer einer asynchronen Methode, die „void“ zurückgibt, kann von der Methode ausgelöste Ausnahmen nicht behandeln, und solche Ausnahmefehler können möglicherweise zu Fehlern in der Anwendung führen.The caller of a void-returning async method can't catch exceptions thrown from the method, and such unhandled exceptions are likely to cause your application to fail. Wenn eine Methode, die Task oder Task<TResult> zurückgibt, eine Ausnahme auslöst, wird die Ausnahme im zurückgegebenen Task gespeichert.If a method that returns a Task or Task<TResult> throws an exception, the exception is stored in the returned task. Die Ausnahme wird erneut ausgelöst, wenn auf den Task gewartet wird.The exception is rethrown when the task is awaited. Stellen Sie daher sicher, dass jede asynchrone Methode, die eine Ausnahme erstellen kann, über den Rückgabetyp Task oder Task<TResult> verfügt und die Aufrufe der Methode erwartet werden.Therefore, make sure that any async method that can produce an exception has a return type of Task or Task<TResult> and that calls to the method are awaited.

Weitere Informationen zum Auffangen von Ausnahmen in asynchronen Methoden finden Sie im Abschnitt Ausnahmen in async-Methoden des Artikels try-catch (C#-Referenz).For more information about how to catch exceptions in async methods, see the Exceptions in async methods section of the try-catch article.

Im folgenden Beispiel wird das Verhalten eines asynchronen Ereignishandlers dargestellt.The following example shows the behavior of an async event handler. Im Beispielcode muss ein asynchroner Ereignishandler dem Hauptthread mitteilen, dass er abgeschlossen wurde.In the example code, an async event handler must let the main thread know when it finishes. Anschließend kann der Hauptthread darauf warten, dass ein asynchroner Ereignishandler abgeschlossen wird, bevor das Programm beendet wird.Then the main thread can wait for an async event handler to complete before exiting the program.

using System;
using System.Threading.Tasks;

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.

Generalisierte asynchrone Rückgabetypen und ValueTask<TResult>Generalized async return types and ValueTask<TResult>

Ab C# 7.0 kann eine asynchrone Methode jeden Typ zurückgeben, der über eine zugängliche GetAwaiter-Methode verfügt.Starting with C# 7.0, an async method can return any type that has an accessible GetAwaiter method.

Da es sich bei Task und Task<TResult> um Verweistypen handelt, kann bei der Speicherbelegung in leistungskritischen Pfaden, besonders bei Zuordnungen in engen Schleifen, die Leistung beeinträchtigt werden.Because Task and Task<TResult> are reference types, memory allocation in performance-critical paths, particularly when allocations occur in tight loops, can adversely affect performance. Die Unterstützung für generalisierte Rückgabetypen bedeutet, dass Sie einen einfachen Werttyp statt eines Verweistypen zurückgeben können, um zusätzliche Speicherbelegungen zu vermeiden.Support for generalized return types means that you can return a lightweight value type instead of a reference type to avoid additional memory allocations.

.NET stellt die Struktur System.Threading.Tasks.ValueTask<TResult> als einfache Implementierung eines generalisierten Werts bereit, der eine Aufgabe zurückgibt..NET provides the System.Threading.Tasks.ValueTask<TResult> structure as a lightweight implementation of a generalized task-returning value. Sie müssen das NuGet-Paket System.Threading.Tasks.Extensions zu Ihrem Projekt hinzufügen, um den Typ System.Threading.Tasks.ValueTask<TResult> zu verwenden.To use the System.Threading.Tasks.ValueTask<TResult> type, you must add the System.Threading.Tasks.Extensions NuGet package to your project. Im folgenden Beispiel wird die Struktur ValueTask<TResult> verwendet, um den Wert von zwei Würfelvorgängen abzurufen.The following example uses the ValueTask<TResult> structure to retrieve the value of two dice rolls.

using System;
using System.Threading.Tasks;

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

Asynchrone Datenströme mit IAsyncEnumerable<T>Async streams with IAsyncEnumerable<T>

Ab C# 8.0 kann eine asynchrone Methode einen asynchronen Datenstrom zurückgeben, der durch IAsyncEnumerable<T> dargestellt wird.Starting with C# 8.0, an async method may return an async stream, represented by IAsyncEnumerable<T>. Ein asynchroner Datenstrom bietet eine Möglichkeit zur Aufzählung von Elementen, die aus einem Datenstrom gelesen wurden, wenn die Elemente mit wiederholten asynchronen Aufrufen in Blöcken generiert werden.An async stream provides a way to enumerate items read from a stream when elements are generated in chunks with repeated asynchronous calls. Das folgende Beispiel zeigt eine asynchrone Methode, die einen asynchronen Datenstrom generiert:The following example shows an async method that generates an async stream:

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

Das oben gezeigte Beispiel liest asynchron Zeilen aus einer Zeichenfolge.The preceding example reads lines from a string asynchronously. Sobald eine Zeile gelesen wurde, zählt der Code jedes Wort in der Zeichenfolge auf.Once each line is read, the code enumerates each word in the string. Aufrufen zählen die einzelnen Wörter mit der await foreach-Anweisung auf.Callers would enumerate each word using the await foreach statement. Die Methode wartet, wenn sie die nächste Zeile aus der Quellzeichenfolge asynchron lesen muss.The method awaits when it needs to asynchronously read the next line from the source string.

Siehe auchSee also