Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 30 additions & 25 deletions docs/csharp/programming-guide/concepts/async/async-return-types.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,62 @@
---
title: "Async Return Types (C#)"
ms.date: 05/29/2017
ms.date: 04/14/2020
ms.assetid: ddb2539c-c898-48c1-ad92-245e4a996df8
---
# Async Return Types (C#)

Async methods can have the following return types:

- <xref:System.Threading.Tasks.Task%601>, for an async method that returns a value.

- <xref:System.Threading.Tasks.Task>, for an async method that performs an operation but returns no value.

- `void`, for an event handler.

- Starting with C# 7.0, any type that has an accessible `GetAwaiter` method. The object returned by the `GetAwaiter` method must implement the <xref:System.Runtime.CompilerServices.ICriticalNotifyCompletion?displayProperty=nameWithType> interface.

- Starting with C# 8.0, <xref:System.Collections.Generic.IAsyncEnumerable%601>, for an async method that returns an *async stream*.

For more information about async methods, see [Asynchronous Programming with async and await (C#)](./index.md).

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.

## <a name="BKMK_TaskTReturnType"></a> Task\<TResult\> Return Type
The <xref:System.Threading.Tasks.Task%601> return type is used for an async method that contains a [return](../../../language-reference/keywords/return.md) (C#) statement in which the operand has type `TResult`.
## Task\<TResult\> Return Type
The <xref:System.Threading.Tasks.Task%601> return type is used for an async method that contains a [return](../../../language-reference/keywords/return.md) (C#) statement in which the operand is `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 <xref:System.Threading.Tasks.Task.FromResult%2A> async method is a placeholder for an operation that returns a string.

[!code-csharp[return-value](../../../../../samples/snippets/csharp/programming-guide/async/async-returns1.cs)]
:::code language="csharp" source="./snippets/async-returns1.cs" id="SnippetFirstExample":::

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](../../../language-reference/operators/await.md).

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 `integerTask` variable in the example. Because `integerTask` is a <xref:System.Threading.Tasks.Task%601>, it contains a <xref:System.Threading.Tasks.Task%601.Result> property of type `TResult`. In this case, `TResult` represents an integer type. When `await` is applied to `integerTask`, the await expression evaluates to the contents of the <xref:System.Threading.Tasks.Task%601.Result%2A> property of `integerTask`. The value is assigned to the `ret` variable.
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. 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 `integerTask` variable in the example. Because `integerTask` is a <xref:System.Threading.Tasks.Task%601>, it contains a <xref:System.Threading.Tasks.Task%601.Result> property of type `TResult`. In this case, `TResult` represents an integer type. When `await` is applied to `integerTask`, the await expression evaluates to the contents of the <xref:System.Threading.Tasks.Task%601.Result%2A> property of `integerTask`. The value is assigned to the `ret` variable.

> [!IMPORTANT]
> The <xref:System.Threading.Tasks.Task%601.Result%2A> 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. <br/> The previous example retrieved the value of the <xref:System.Threading.Tasks.Task%601.Result%2A> property to block the main thread so that the `ShowTodaysInfo` method could finish execution before the application ended.

[!code-csharp[return-value](../../../../../samples/snippets/csharp/programming-guide/async/async-returns1a.cs#1)]
## <a name="BKMK_TaskReturnType"></a> Task Return Type
:::code language="csharp" source="./snippets/async-returns1a.cs" id="SnippetSecondVersion":::

## 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 <xref:System.Threading.Tasks.Task>. Such methods return `void` if they run synchronously. If you use a <xref:System.Threading.Tasks.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 <xref:System.Threading.Tasks.Task> object. This enables `WaitAndApologize` to be awaited. Note that the <xref:System.Threading.Tasks.Task> type doesn't include a `Result` property because it has no return value.
In the following example, the `WaitAndApologize` async method doesn't contain a `return` statement, so the method returns a <xref:System.Threading.Tasks.Task> object. Returning a `Task` enables `WaitAndApologize` to be awaited. The <xref:System.Threading.Tasks.Task> type doesn't include a `Result` property because it has no return value.

:::code language="csharp" source="./snippets/async-returns2.cs" id="SnippetTaskReturn":::

[!code-csharp[return-value](../../../../../samples/snippets/csharp/programming-guide/async/async-returns2.cs)]

`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 <xref:System.Threading.Tasks.Task%601> 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.

[!code-csharp[return-value](../../../../../samples/snippets/csharp/programming-guide/async/async-returns2a.cs#1)]
:::code language="csharp" source="./snippets/async-returns2a.cs" id="SnippetAwaitTask":::

## <a name="BKMK_VoidReturnType"></a> Void return type
## 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 <xref:System.Threading.Tasks.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.
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 <xref:System.Threading.Tasks.Task> instead, because an async method that returns `void` can't be awaited. Any caller of such a method must continue to completion without waiting for the called async method to finish. 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 <xref:System.Threading.Tasks.Task> or <xref:System.Threading.Tasks.Task%601>, 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 <xref:System.Threading.Tasks.Task> or <xref:System.Threading.Tasks.Task%601> and that calls to the method are awaited.
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. If a method that returns a <xref:System.Threading.Tasks.Task> or <xref:System.Threading.Tasks.Task%601> throws an exception, the exception is stored in the returned task. The exception is rethrown when the task is awaited. Therefore, make sure that any async method that can produce an exception has a return type of <xref:System.Threading.Tasks.Task> or <xref:System.Threading.Tasks.Task%601> 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](../../../language-reference/keywords/try-catch.md#exceptions-in-async-methods) section of the [try-catch](../../../language-reference/keywords/try-catch.md) topic.
For more information about how to catch exceptions in async methods, see the [Exceptions in Async Methods](../../../language-reference/keywords/try-catch.md#exceptions-in-async-methods) section of the [try-catch](../../../language-reference/keywords/try-catch.md) article.

The following example shows the behavior of an async event handler. Note that in the example code, an async event handler must let the main thread know when it finishes. Then the main thread can wait for an async event handler to complete before exiting the program.
The following example shows the behavior of an async event handler. In the example code, an async event handler must let the main thread know when it finishes. Then the main thread can wait for an async event handler to complete before exiting the program.

[!code-csharp[return-value](../../../../../samples/snippets/csharp/programming-guide/async/async-returns3.cs)]
:::code language="csharp" source="./snippets/async-returns3.cs":::

## Generalized async return types and ValueTask\<TResult\>

Expand All @@ -69,7 +66,15 @@ Because <xref:System.Threading.Tasks.Task> and <xref:System.Threading.Tasks.Task

.NET provides the <xref:System.Threading.Tasks.ValueTask%601?displayProperty=nameWithType> structure as a lightweight implementation of a generalized task-returning value. To use the <xref:System.Threading.Tasks.ValueTask%601?displayProperty=nameWithType> type, you must add the `System.Threading.Tasks.Extensions` NuGet package to your project. The following example uses the <xref:System.Threading.Tasks.ValueTask%601> structure to retrieve the value of two dice rolls.

[!code-csharp[return-value](../../../../../samples/snippets/csharp/programming-guide/async/async-valuetask.cs)]
:::code language="csharp" source="./snippets/async-valuetask.cs":::

## Async streams with IAsyncEnumerable\<T\>

Starting with C# 8.0, an async method may return an *async stream*, represented by <xref:System.Collections.Generic.IAsyncEnumerable%601>. An async stream provides a way to enumerate items read from a stream when elements are generated in chunks with repeated asynchronous calls. The following example shows an async method that generates an async stream:

:::code language="csharp" source="./snippets/AsyncStreams.cs" id="SnippetGenerateAsyncStream":::

The preceding example reads lines from a string asynchronously. Once each line is read, the code enumerates each word in the string. Callers would enumerate each word using the `await foreach` statement. The method awaits when it needs to asynchronously read the next line from the source string.

## See also

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Nullable>Enable</Nullable>
<StartupObject>AsyncExamples.Program</StartupObject>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

namespace AsyncExamples
{

public static class AsyncStreamExample
{

public static async Task Examples()
{
await foreach (var word in ReadWordsFromStream())
Console.WriteLine(word);
}

// <SnippetGenerateAsyncStream>
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();
}
}
// </SnippetGenerateAsyncStream>
}
}
17 changes: 17 additions & 0 deletions docs/csharp/programming-guide/concepts/async/snippets/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Threading.Tasks;

namespace AsyncExamples
{
public static class Program
{
static async Task Main()
{
await FirstExample.ShowTodaysInfo();
await SecondExample.ShowTodaysInfo();
await ExampleTask.DisplayCurrentInfo();
await AwaitTaskExample.DisplayCurrentInfo();
await AsyncVoidExample.Main();
await AsyncStreamExample.Examples();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Linq;
using System.Threading.Tasks;

public class FirstExample
{
// <SnippetFirstExample>
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
// </SnippetFirstExample>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Linq;
using System.Threading.Tasks;

public class SecondExample
{
public static async Task<string> ShowTodaysInfo()
{
// <SnippetSecondVersion>
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;
// </SnippetSecondVersion>
}

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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Threading.Tasks;

public class ExampleTask
{
// <SnippetTaskReturn>
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.
// </SnippetTaskReturn>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Threading.Tasks;

public class AwaitTaskExample
{
public static async Task DisplayCurrentInfo()
{
// <SnippetAwaitTask>
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);
// </SnippetAwaitTask>
}

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.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

public class NaiveButton
{
public event EventHandler Clicked;
public event EventHandler? Clicked;

public void Click()
{
Expand All @@ -15,9 +15,9 @@ public void Click()

public class AsyncVoidExample
{
static TaskCompletionSource<bool> tcs;
static TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

static async Task Main()
public static async Task Main()
{
tcs = new TaskCompletionSource<bool>();
var secondHandlerFinished = tcs.Task;
Expand All @@ -34,14 +34,14 @@ static async Task Main()
await secondHandlerFinished;
}

private static void Button_Clicked_1(object sender, EventArgs e)
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)
private static async void Button_Clicked_2_Async(object? sender, EventArgs e)
{
Console.WriteLine(" Handler 2 is starting...");
Task.Delay(100).Wait();
Expand All @@ -51,7 +51,7 @@ private static async void Button_Clicked_2_Async(object sender, EventArgs e)
tcs.SetResult(true);
}

private static void Button_Clicked_3(object sender, EventArgs e)
private static void Button_Clicked_3(object? sender, EventArgs e)
{
Console.WriteLine(" Handler 3 is starting...");
Task.Delay(100).Wait();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

class Program
{
static Random rnd;
static Random? rnd;

static void Main()
{
Expand Down
Loading