Async Return Types (C#)

Async methods can have the following return types:

For more information about async methods, see Asynchronous Programming with async and await (C#).

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<TResult> Return Type

The Task<TResult> return type is used for an async method that contains a return (C#) statement in which the operand has type TResult.

In the following example, the GetLeisureHours async method contains a return statement that returns an integer. Therefore, the method declaration must specify a return type of Task<int>. 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 >

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. For more information about await expressions, see await.

You can better understand how this happens by separating the call to GetLeisureHours from the application of await, as the following code shows. A call to method GetLeisureHours that isn't immediately awaited returns a Task<int>, as you would expect from the declaration of the method. The task is assigned to the infoTask variable in the example. Because infoTask is a Task<TResult>, it contains a Result property of type TResult. In this case, TResult represents an integer type. When await is applied to infoTask, the await expression evaluates to the contents of the Result property of infoTask. The value is assigned to the ret variable.

Important

The Result property is a blocking property. 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 most cases, you should access the value by using await instead of accessing the property directly.
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}";

Task Return Type

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. Such methods return void if they run synchronously. 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.

In the following example, the WaitAndApologize async method doesn't contain a return statement, so the method returns a Task object. This enables WaitAndApologize to be awaited. 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 is awaited by using an await statement instead of an await expression, similar to the calling statement for a synchronous void-returning method. The application of an await operator in this case doesn't produce a value.

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

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

Void return type

You use the void return type in asynchronous event handlers, which require a void return type. 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. 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.

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

For more information about how to catch exceptions in async methods, see the Exceptions in Async Methods section of the try-catch topic.

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

Generalized async return types and ValueTask<TResult>

Starting with C# 7.0, an async method can return any type that has an accessible GetAwaiter method.

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. 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 provides the System.Threading.Tasks.ValueTask<TResult> structure as a light-weight implementation of a generalized task-returning value. To use the System.Threading.Tasks.ValueTask<TResult> type, you must add the System.Threading.Tasks.Extensions NuGet package to your project. 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

See also

FromResult
Walkthrough: Accessing the Web by Using async and await (C#)
Control Flow in Async Programs (C#)
async
await