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<TResult> für eine asynchrone Methode, die einen Wert zurückgibt.Task<TResult>, for an async method that returns a value.

  • 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.

  • void für einen Ereignishandler.void, for an event handler.

  • Ab C# 7: Jeder Typ, der über eine zugängliche GetAwaiter-Methode verfügt.Starting with C# 7, 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.

Weitere Informationen über die Methode „Async“ finden Sie unter Asynchronous Programming with async and await (C#) (Asynchrone Programmierung mit Async und Await (C#)).For more information about async methods, see Asynchronous Programming with async and await (C#).

Jeder Rückgabetyp wird in einem der folgenden Abschnitte untersucht und am Ende des Themas wird ein vollständiges Beispiel aller drei Typen verwenden.Each return type is examined in one of the following sections, and you can find a full example that uses all three types at the end of the topic.

Task(T)-RückgabetypTask(T) Return Type

Der Task<TResult>-Rückgabetyp wird für eine asynchrone Methode verwendet, die eine Rückgabeanweisung (C#) enthält, in der der Operand den Typ TResult hat.The Task<TResult> return type is used for an async method that contains a return (C#) statement in which the operand has type TResult.

Im folgenden Beispiel enthält die asynchrone GetLeisureHours-Methode eine return-Anweisung, die eine ganze Zahl zurückgibt.In the following example, the GetLeisureHours async 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 eine Zeichenfolge zurückgibt.The FromResult async method is a placeholder for an operation that returns a string.

using System;
using System.Linq;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      Console.WriteLine(ShowTodaysInfo().Result);
   }

   private static async Task<string> ShowTodaysInfo()
   {
      string ret = $"Today is {DateTime.Today:D}\n" +
                   "Today's hours of leisure: " +
                   $"{await GetLeisureHours()}";
      return ret;
   }

   static async Task<int> GetLeisureHours()  
   {  
       // Task.FromResult is a placeholder for actual work that returns a string.  
       var today = await Task.FromResult<string>(DateTime.Now.DayOfWeek.ToString());  
     
       // The method then can process the result in some way.  
       int leisureHours;  
       if (today.First() == 'S')  
           leisureHours = 16;  
       else  
           leisureHours = 5;  
     
       return leisureHours;  
   }  
}
// The example displays output like the following:
//       Today is Wednesday, May 24, 2017
//       Today's hours of leisure: 5
// </Snippet >

Wenn GetLeisureHours 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 GetLeisureHours 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.

Sie können die Vorgehensweise besser verstehen, wenn Sie den Aufruf von GetLeisureHours von der Anwendung von await trennen, wie der folgenden Code zeigt.You can better understand how this happens by separating the call to GetLeisureHours from the application of await, as the following code shows. Ein Aufruf der TaskOfT_MethodAsync-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 TaskOfT_MethodAsync 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 integerTask-Variablen zugewiesen.The task is assigned to the integerTask variable in the example. Da integerTask eine Task<TResult> ist, enthält es eine Result-Eigenschaft des Typs TResult.Because integerTask is a Task<TResult>, it contains a Result property of type TResult. In diesem Fall stellt TResult einen ganzzahligen Typ dar.In this case, TResult represents an integer type. Wenn await auf integerTask angewendet wird, wertet der „await“-Ausdruck den Inhalt der Eigenschaft Result von integerTask aus.When await is applied to integerTask, the await expression evaluates to the contents of the Result property of integerTask. Der Wert wird der result2-Variablen zugewiesen.The value is assigned to the result2 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 Eigenschaft Result abgerufen, um den Hauptthread zu blockieren, sodass die Methode ShowTodaysInfo die Ausführung beenden konnte, bevor die Anwendung beendet wurde.The previous example retrieved the value of the Result property to block the main thread so that the ShowTodaysInfo method could finish execution before the application ended.

var infoTask = GetLeisureHours();

// You can do other work that does not rely on integerTask before awaiting.

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

Aufgabenrü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 asynchrone Methode WaitAndApologize keine return-Anweisung, daher gibt die Methode ein Task-Objekt zurück.In the following example, the WaitAndApologize async method doesn't contain a return statement, so the method returns a Task object. Dadurch wird ermöglicht, dass auf WaitAndApologize erwartet wird.This enables WaitAndApologize to be awaited. Beachten Sie, dass der Typ Task keine Result-Eigenschaft enthält, da er nicht über einen Rückgabewert verfügt.Note that the Task type doesn't include a Result property because it has no return value.

using System;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      DisplayCurrentInfo().Wait();
   }

   static async Task DisplayCurrentInfo()
   {
      await WaitAndApologize();
      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 WaitAndApologize()
   {
      // Task.Delay is a placeholder for actual work.  
      await Task.Delay(2000);  
      // Task.Delay delays the following line by two seconds.  
      Console.WriteLine("\nSorry for the delay. . . .\n");  
   }
}
// The example displays the following output:
//       Sorry for the delay. . . .
//       
//       Today is Wednesday, May 24, 2017
//       The current time is 15:25:16.2935649
//       The current temperature is 76 degrees.

WaitAndApologize wird erwartet, indem eine „await“-Anweisung anstelle eines „await“-Ausdrucks verwendet wird, ähnlich der Aufrufanweisung einer Methode, die „void“ zurückgibt.WaitAndApologize 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.

Wie im vorherigen Beispiel Task<TResult> können Sie den Aufruf von Task_MethodAsync mit einem Erwartungsoperator trennen, wie der folgende Code zeigt.As in the previous Task<TResult> example, you can separate the call to Task_MethodAsync 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 WaitAndApologize vom Erwarten der Aufgabe, die die Methode zurückgibt.The following code separates calling the WaitAndApologize method from awaiting the task that the method returns.

Task wait = WaitAndApologize();

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 wait;
Console.WriteLine(output);

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. Da andere Methoden als Ereignishandler keinen Wert zurückgeben, sollten Sie stattdessen eine Task zurückgeben, da eine void zurückgebende asynchrone Methode nicht erwartet werden kann.For methods other than event handlers 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 in der Lage sein, in seiner Ausführung bis zum Abschluss fortzufahren, ohne auf die aufgerufene asynchrone Methode zu warten, und der Aufrufer muss unabhängig von den Werten oder Ausnahmen sein, die die asynchrone Methode generiert.Any caller of such a method must be able to continue to completion without waiting for the called async method to finish, and the caller must be independent of any values or exceptions that the async method generates.

Der Aufrufer einer "void" zurückgebenden asynchronen Methode kann die 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 that are thrown from the method, and such unhandled exceptions are likely to cause your application to fail. Wenn eine Ausnahme in einer asynchronen Methode auftritt, die Task oder Task<TResult> zurückgibt, wird die Ausnahme in der zurückgegebenen Aufgabe gespeichert und erneut ausgelöst, wenn die Aufgabe erwartet wird.If an exception occurs in an async method that returns a Task or Task<TResult>, the exception is stored in the returned task and is rethrown when the task is awaited. Daher stellen Sie sicher, dass jede asynchrone Methode, die eine Ausnahme erstellen kann, über einen 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 async-Methoden finden Sie unter try-catch.For more information about how to catch exceptions in async methods, see try-catch .

Das folgende Beispiel definiert einen asynchronen Ereignishandler.The following eample defines an async event handler.

using System;
using System.Threading.Tasks;

public class Counter
{
   private int threshold = 0;
   private int iterations = 0;
   private int ctr = 0;

   event EventHandler<EventArgs> ThresholdReached;

   public Counter(int threshold)
   {
      this.threshold = threshold;
      ThresholdReached += thresholdReachedEvent;
   }

   public async Task<int> StartCounting(int limit)
   {
      iterations = 1;
      for (int index = 0; index <= limit; index++) {
         if (ctr == threshold)
            thresholdReachedEvent(this, EventArgs.Empty);
         ctr++;
         await Task.Delay(500);
      }
      int retval = ctr + (iterations - 1) * threshold;
      Console.WriteLine($"On iteration {iterations}, reached {limit}");
      return retval;    
   }

   async void thresholdReachedEvent(object sender, EventArgs e)
   {
       Console.WriteLine($"Reached {ctr}. Resetting...");
       await Task.Delay(1000);
       ctr = 0;
       iterations++;
   }
}

public class Example
{
   public static void Main()
   {
      RunCounter().Wait();
   }

   private static async Task RunCounter()
   {
      var count = new Counter(5);
      await count.StartCounting(8);
   }
}

Generalisierte asynchrone Rückgabetypen und ValueTaskGeneralized async return types and ValueTask

Ab C# 7 kann eine asynchrone Methode jeden Typ zurückgeben, der über eine zugängliche GetAwaiter-Methode verfügt.Starting with C# 7, 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 light-weight 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 Random rnd;
   
   static void Main()
   {
      Console.WriteLine($"You rolled {GetDiceRoll().Result}");
   }
   
   private static async ValueTask<int> GetDiceRoll()
   {
      Console.WriteLine("...Shaking the dice...");
      int roll1 = await Roll();
      int roll2 = await Roll();
      return roll1 + roll2; 
   } 
   
   private static async ValueTask<int> Roll()
   {
      if (rnd == null)
         rnd = new Random();
      
      await Task.Delay(500);
      int diceRoll = rnd.Next(1, 7);
      return diceRoll;
   } 
}
// The example displays output like the following:
//       ...Shaking the dice...
//       You rolled 8

Siehe auchSee also

FromResultFromResult
Exemplarische Vorgehensweise: Zugreifen auf das Web mit async und await (C#) Walkthrough: Accessing the Web by Using async and await (C#)
Ablaufsteuerung in asynchronen Programmen Control Flow in Async Programs (C#)
async async
awaitawait