Back to: C#.NET Tutorials For Beginners and Professionals
Generalized Async Return Types in C# with Examples
In this article, I am going to discuss the Generalized Async Return Types in C# with examples. Please read our previous article where we discuss ref locals and ref returns in C# with examples. Before understanding the generalized async return types in C#, let’s have a look at asynchronous programming and try to understand how it works.
If you have worked with the async methods, then you may know the async methods can have the following return types:
- Task<TResult>, this return type is used when the async method returns a value.
- Task, this return type is used when the async method does not return any value.
- void, this return type is used for an event handler.
Let us discussed each of these return types with examples.
The async method returning Task<T> in C#
We need to use the Task<TResult> return type when the async method is going to return a value after the execution of the method using a return statement. In the following example, the GetLeisureHours() async method returns an integer value by using the return statement. So, we specify the GetLeisureHours() async method return type as Task<int>.
The ShowTodaysInfo() async method is going to return a string. So, the return type of this async method is Task<string>. One more point that you need to remember is whenever you want to call an async method from another async method then you need to use the await keyword while calling the method. In our example, we are calling the GetLeisureHours() async method from the ShowTodaysInfo() async method and you can see while the GetLeisureHours() async method we use the await keyword. The FromResult async method is a placeholder for an operation that returns a string. The complete example is given below.
public class Example { public static void Main() { Console.WriteLine(ShowTodaysInfo().Result); Console.WriteLine("Press any key to exist."); Console.ReadKey(); } 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; } }
Output:
For a better understanding of how this happens let’s separate the call to GetLeisureHours() async method from the application of await as the following code shows.
public class Example { public static void Main() { Console.WriteLine(ShowTodaysInfo().Result); Console.WriteLine("Press any key to exist."); Console.ReadKey(); } private static async Task<string> ShowTodaysInfo() { 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}"; 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; } }
Output:
Note: The Result property that we used to retrieve the value is a blocking property. It means if we try to access the value before the async method completes its task, then the thread which is currently active is blocked until the task completes and the value is available. In most real-time applications, we need to access the value by using the “await” keyword instead of accessing the property directly. But the point that you need to keep in mind is that you can only use the await property from within an async method.
The async method returning Task in C#
We need to use the Task return type when the async method is not returning any value after the execution of the method. It means the async method either does not have a return statement in it or it may contain a return statement that doesn’t return any value. Such type of async methods returns void if they run synchronously.
If we have an async method with Task return type and if we want our caller method to wait until the async method completes its execution then we need to use the await operator while calling the async method.
In the following example, the WaitAndApologize() async method return type is Task as it doesn’t have a return statement. We are calling this WaitAndApologize() async method from the DisplayCurrentInfo() async method. As we want to wait until the WaitAndApologize() method completes its execution so when calling this method from within the DisplayCurrentInfo() method we use the await operator.
Again from our Main() method, we are calling the DisplayCurrentInfo() async method and our requirement is to wait until the DisplayCurrentInfo() method complete its execution, so here we using the Wait() method while calling the DisplayCurrentInfo() method. We can not use the await operator here because the Main method is not an async method. As we know we can use the await operator only within an async method.
public class Example { public static void Main() { DisplayCurrentInfo().Wait(); Console.WriteLine("Press any key to exist."); Console.ReadKey(); } 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"); } }
Output:
The following code separates calling the WaitAndApologize method from awaiting the task that the method returns.
public class Example { public static void Main() { DisplayCurrentInfo().Wait(); Console.WriteLine("Press any key to exist."); Console.ReadKey(); } static async Task DisplayCurrentInfo() { 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); } 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"); } }
Output:
Async method returning void in C#
We need to use the void return type in C# when the async method does not return any value. Then you may have one question in your mind what is the difference between Task and void return types as both are going to be used when the async method does not return any value.
The difference is that if you use the void return type then the async method cannot be awaited. That means the caller of such method (void return async method) do not have any option to wait for the async method to complete its work. They simply call the async method and continue their work. So if you have methods other than event handlers that don’t return any value, it’s always advisable to use Task return type instead of void.
Example: Async method returning void in C#
Please have a look at the below example.
public class Example { public static void Main() { RunCounter().Wait(); Console.WriteLine("Press any key to exist."); Console.ReadKey(); } private static async Task RunCounter() { var count = new Counter(5); await count.StartCounting(8); } } 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++; } }
Output:
I hope now you have some idea regarding the async method in C#. So, let us move to our main topic of this article i.e.Generalized Async Return Types in C#.
Understanding Generalized Async Return Types in C#
As of now, we have discussed the async method with return type Task, Task<T>, and void. The most important point that you need to keep in mind is that the Task is a class. We also know the reference types behave differently in C#. In some situations, it is better to return anything rather than a Task.
The generalized async returns types in C# mean you can return a lightweight value type instead of a reference type to avoid additional memory allocations. From C# 7, there is an inbuilt value type ValueTask <T> which can be used instead of Task<T>.
.NET Framework provides the System.Threading.Tasks.ValueTask<TResult> 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.
Example: Generalized Async Return Types in C#
Let us understand Generalized Async Return Types in C# concept with an example. Please have a look at the below example. As you can see in the below example, instead of using Task<T>, now we are using ValueTask<T> which is a value type, not a reference type and because of this it will have less memory and provides better performance as compared to Task<T>.
using System; using System.Linq; using System.Threading.Tasks; namespace GeneralizedAsyncReturnTypes { public class Example { public static void Main() { Console.WriteLine(ShowTodaysInfo().Result); Console.WriteLine("Press any key to exist."); Console.ReadKey(); } private static async ValueTask<string> ShowTodaysInfo() { 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}"; return ret; } static async ValueTask<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; } } }
Output:
You may be thinking that we are talking about the term generalized async, but here we are using only ValueTask<T>. So, I would like to clarify your doubt that you can also create your own type which can be the return type of your async method. However, if you do not want to create your own type, then you can use the ValueTask<T> which is already available.
In the next article, I am going to discuss the Expression Bodied Members in C# with Examples. Here, in this article, I try to explain Generalized Async Return Types in C# with Examples. I hope this article will help you with your need. I would like to have your feedback. Please post your feedback, question, or comments about this Generalized Async Return Types in C# with Examples article.