Types de retour async (C#)Async Return Types (C#)

Les méthodes async peuvent avoir les types de retour suivants :Async methods can have the following return types:

  • Task<TResult>, pour une méthode async qui retourne une valeur.Task<TResult>, for an async method that returns a value.
  • Task, pour une méthode async qui effectue une opération mais ne retourne aucune valeur.Task, for an async method that performs an operation but returns no value.
  • void, pour un gestionnaire d’événements.void, for an event handler.
  • À compter de C# 7.0, tout type ayant une méthode GetAwaiter accessible.Starting with C# 7.0, any type that has an accessible GetAwaiter method. L’objet retourné par la méthode GetAwaiter doit implémenter l’interface System.Runtime.CompilerServices.ICriticalNotifyCompletion.The object returned by the GetAwaiter method must implement the System.Runtime.CompilerServices.ICriticalNotifyCompletion interface.
  • À compter de C# 8,0, IAsyncEnumerable<T> , pour une méthode Async qui retourne un flux asynchrone.Starting with C# 8.0, IAsyncEnumerable<T>, for an async method that returns an async stream.

Pour plus d’informations sur les méthodes async, consultez Programmation asynchrone avec async et await (C#).For more information about async methods, see Asynchronous Programming with async and await (C#).

Task<TResult>, type de retourTask<TResult> Return Type

Le Task<TResult> type de retour est utilisé pour une méthode Async qui contient une instruction Return (C#) dans laquelle l’opérande est TResult .The Task<TResult> return type is used for an async method that contains a return (C#) statement in which the operand is TResult.

Dans l’exemple suivant, la méthode async GetLeisureHours contient une instruction return qui retourne un entier.In the following example, the GetLeisureHours async method contains a return statement that returns an integer. La déclaration de méthode doit donc spécifier un type de retour Task<int>.Therefore, the method declaration must specify a return type of Task<int>. La méthode async FromResult est un espace réservé pour une opération qui retourne une chaîne.The FromResult async method is a placeholder for an operation that returns a string.

public 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

Quand la méthode GetLeisureHours est appelée à partir d’une expression await dans la méthode ShowTodaysInfo, cette expression récupère la valeur entière (la valeur de leisureHours) qui est stockée dans la tâche retournée par la méthode GetLeisureHours.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. Pour plus d’informations sur les expressions await, consultez await.For more information about await expressions, see await.

Vous pouvez mieux comprendre comment await récupère le résultat d’un Task<T> en séparant l’appel à GetLeisureHours de l’application de await , comme le montre le code suivant.You can better understand how await retrieves the result from a Task<T> by separating the call to GetLeisureHours from the application of await, as the following code shows. Un appel à la méthode GetLeisureHours qui n’est pas immédiatement attendue retourne un type Task<int>, comme vous pourriez l’attendre de la déclaration de la méthode.A call to method GetLeisureHours that isn't immediately awaited returns a Task<int>, as you would expect from the declaration of the method. La tâche est assignée à la variable integerTask dans l’exemple.The task is assigned to the integerTask variable in the example. Comme integerTask est un Task<TResult>, il contient une propriété Result de type TResult.Because integerTask is a Task<TResult>, it contains a Result property of type TResult. Dans ce cas, TResult représente un type entier.In this case, TResult represents an integer type. Quand await est appliqué à integerTask, l’expression await prend pour la valeur le contenu de la propriété Result de integerTask.When await is applied to integerTask, the await expression evaluates to the contents of the Result property of integerTask. La valeur est assignée à la variable ret.The value is assigned to the ret variable.

Important

Result est une propriété bloquante.The Result property is a blocking property. Si vous essayez d’y accéder avant la fin de sa tâche, le thread actif est bloqué tant que la tâche n’est pas terminée et que la valeur n’est pas disponible.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. Dans la plupart des cas, vous devez accéder à la valeur avec await au lieu d’accéder directement à la propriété.In most cases, you should access the value by using await instead of accessing the property directly.
L’exemple précédent récupérait la valeur de la propriété Result pour bloquer le thread principal afin de permettre à la méthode ShowTodaysInfo de terminer son exécution avant la fin de l’application.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 integerTask = 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 integerTask}";
return ret;

Type de retour taskTask Return Type

Les méthodes async qui ne contiennent pas d’instruction return ou qui contiennent une instruction return ne retournant pas d’opérande ont généralement un type de retour 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. De telles méthodes retournent void si elles s’exécutent de façon synchrone.Such methods return void if they run synchronously. Si vous utilisez un type de retour Task pour une méthode async, une méthode d’appel peut utiliser un opérateur await pour suspendre l’achèvement de l’appelant jusqu’à ce que la méthode async soit terminée.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.

Dans l’exemple suivant, la méthode async WaitAndApologize ne contient pas d’instruction return. Par conséquent, elle retourne un objet Task.In the following example, the WaitAndApologize async method doesn't contain a return statement, so the method returns a Task object. Le retour Task d’un permet WaitAndApologize à d’être attendu.Returning a Task enables WaitAndApologize to be awaited. Le Task type n’inclut Result pas de propriété, car il n’a pas de valeur de retour.The Task type doesn't include a Result property because it has no return value.

public 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 est attendue avec une instruction await au lieu d’une expression await, comme pour l’instruction d’appel d’une méthode synchrone retournant un type void.WaitAndApologize is awaited by using an await statement instead of an await expression, similar to the calling statement for a synchronous void-returning method. Dans ce cas, l’application d’un opérateur await ne génère pas de valeur.The application of an await operator in this case doesn't produce a value.

Comme dans l’exemple Task<TResult> précédent, vous pouvez séparer l’appel à WaitAndApologize de l’application d’un opérateur await, comme l’illustre le code suivant.As in the previous Task<TResult> example, you can separate the call to WaitAndApologize from the application of an await operator, as the following code shows. Cependant, n’oubliez pas qu’un type Task n’a pas de propriété Result et qu’aucune valeur n’est générée quand un opérateur await est appliqué à un type Task.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.

Le code suivant sépare l’appel à la méthode WaitAndApologize de l’attente de la tâche que retourne la méthode.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);

Type de retour voidVoid return type

Vous utilisez le type de retour void dans les gestionnaires d’événements asynchrones, lesquels exigent un type de retour void.You use the void return type in asynchronous event handlers, which require a void return type. Pour les méthodes autres que les gestionnaires d’événements qui ne retournent pas de valeur, vous devez retourner Task à la place, car une méthode async qui retourne void ne peut pas être attendue.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. Tout appelant d’une telle méthode doit continuer à se terminer sans attendre la fin de la méthode Async appelée.Any caller of such a method must continue to completion without waiting for the called async method to finish. L’appelant doit être indépendant des valeurs ou des exceptions générées par la méthode Async.The caller must be independent of any values or exceptions that the async method generates.

L’appelant d’une méthode Async qui retourne void ne peut pas intercepter les exceptions levées à partir de la méthode, et ces exceptions non gérées sont susceptibles de provoquer l’échec de votre application.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. Si une méthode qui retourne un Task ou Task<TResult> lève une exception, l’exception est stockée dans la tâche retournée.If a method that returns a Task or Task<TResult> throws an exception, the exception is stored in the returned task. L’exception est levée une nouvelle fois lorsque la tâche est attendue.The exception is rethrown when the task is awaited. Par conséquent, veillez à ce qu’une méthode async capable de générer une exception ait le type de retour Task ou Task<TResult> et que les appels à la méthode soient attendus.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.

Pour plus d’informations sur la façon d’intercepter des exceptions dans les méthodes Async, consultez la section exceptions dans les méthodes Async de l’article try-catch .For more information about how to catch exceptions in async methods, see the Exceptions in Async Methods section of the try-catch article.

L’exemple suivant illustre le comportement d’un gestionnaire d’événements asynchrones.The following example shows the behavior of an async event handler. Dans l’exemple de code, un gestionnaire d’événements asynchrones doit permettre au thread principal de savoir quand il se termine.In the example code, an async event handler must let the main thread know when it finishes. Le thread principal peut donc attendre la fin d’un gestionnaire d’événements asynchrones avant de quitter le programme.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 TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

    public static async Task Main()
    {
        tcs = new TaskCompletionSource<bool>();
        var secondHandlerFinished = tcs.Task;

        var button = new NaiveButton();
        button.Clicked += Button_Clicked_1;
        button.Clicked += Button_Clicked_2_Async;
        button.Clicked += Button_Clicked_3;

        Console.WriteLine("About to click a button...");
        button.Click();
        Console.WriteLine("Button's Click method returned.");

        await secondHandlerFinished;
    }

    private static void Button_Clicked_1(object? sender, EventArgs e)
    {
        Console.WriteLine("   Handler 1 is starting...");
        Task.Delay(100).Wait();
        Console.WriteLine("   Handler 1 is done.");
    }

    private static async void Button_Clicked_2_Async(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.");
        tcs.SetResult(true);
    }

    private static void Button_Clicked_3(object? sender, EventArgs e)
    {
        Console.WriteLine("   Handler 3 is starting...");
        Task.Delay(100).Wait();
        Console.WriteLine("   Handler 3 is done.");
    }
}

// Expected output:
// About to click a button...
// 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.
// Button's Click method returned.
//    Handler 2 is done.

Types de retour async généralisés et ValueTask<TResult>Generalized async return types and ValueTask<TResult>

À compter de C# 7.0, une méthode async peut retourner tout type ayant une méthode GetAwaiter accessible.Starting with C# 7.0, an async method can return any type that has an accessible GetAwaiter method.

Étant donné que Task et Task<TResult> sont des types référence, l’allocation de mémoire dans des chemins critiques pour les performances, en particulier quand les allocations se produisent dans des boucles serrées, peuvent nuire aux performances.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. La prise en charge de types de retour généralisés signifie que vous pouvez retourner un type valeur léger au lieu d’un type référence pour éviter des allocations de mémoire supplémentaires.Support for generalized return types means that you can return a lightweight value type instead of a reference type to avoid additional memory allocations.

Le .NET fournit la structure System.Threading.Tasks.ValueTask<TResult> comme implémentation légère d’une valeur retournant des tâches généralisées..NET provides the System.Threading.Tasks.ValueTask<TResult> structure as a lightweight implementation of a generalized task-returning value. Pour utiliser le type System.Threading.Tasks.ValueTask<TResult>, vous devez ajouter le package NuGet System.Threading.Tasks.Extensions à votre projet.To use the System.Threading.Tasks.ValueTask<TResult> type, you must add the System.Threading.Tasks.Extensions NuGet package to your project. L’exemple suivant utilise la structure ValueTask<TResult> pour récupérer la valeur de deux dés.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 async Task Main()
   {
      Console.WriteLine($"You rolled {await GetDiceRollAsync()}");
   }

   private static async ValueTask<int> GetDiceRollAsync()
   {
      Console.WriteLine("...Shaking the dice...");
      int roll1 = await RollAsync();
      int roll2 = await RollAsync();
      return roll1 + roll2;
   }

   private static async ValueTask<int> RollAsync()
   {
      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

Flux asynchrones avec IAsyncEnumerable < T>Async streams with IAsyncEnumerable<T>

À compter de C# 8,0, une méthode Async peut retourner un flux asynchrone, représenté par IAsyncEnumerable<T> .Starting with C# 8.0, an async method may return an async stream, represented by IAsyncEnumerable<T>. Un flux asynchrone fournit un moyen d’énumérer les éléments lus à partir d’un flux lorsque des éléments sont générés dans des segments avec des appels asynchrones répétés.An async stream provides a way to enumerate items read from a stream when elements are generated in chunks with repeated asynchronous calls. L’exemple suivant montre une méthode Async qui génère un flux asynchrone :The following example shows an async method that generates an async stream:

private static async IAsyncEnumerable<string> ReadWordsFromStream()
{
    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)
    {
        var words = line.Split(' ',StringSplitOptions.RemoveEmptyEntries);
        foreach (var word in words)
        {
            yield return word;
        }
        line = await readStream.ReadLineAsync();
    }
}

L’exemple précédent lit les lignes d’une chaîne de façon asynchrone.The preceding example reads lines from a string asynchronously. Une fois chaque ligne lue, le code énumère chaque mot de la chaîne.Once each line is read, the code enumerates each word in the string. Les appelants énumèrent chaque mot à l’aide de l' await foreach instruction.Callers would enumerate each word using the await foreach statement. La méthode attend quand elle doit lire de façon asynchrone la ligne suivante à partir de la chaîne source.The method awaits when it needs to asynchronously read the next line from the source string.

Voir aussiSee also