diff --git a/docs/csharp/programming-guide/concepts/async/async-return-types.md b/docs/csharp/programming-guide/concepts/async/async-return-types.md index 6f35208d7accb..cedabc56f1b77 100644 --- a/docs/csharp/programming-guide/concepts/async/async-return-types.md +++ b/docs/csharp/programming-guide/concepts/async/async-return-types.md @@ -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: - , for an async method that returns a value. - - , 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 interface. - +- Starting with C# 8.0, , 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. - -## Task\ Return Type -The 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\ Return Type +The 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`. The 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`, 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 , it contains a 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 property of `integerTask`. The value is assigned to the `ret` variable. +You can better understand how `await` retrieves the result from a `Task` 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`, 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 , it contains a 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 property of `integerTask`. The value is assigned to the `ret` variable. > [!IMPORTANT] > The 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 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)] - -## 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 . Such methods return `void` if they run synchronously. If you use a 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 object. This enables `WaitAndApologize` to be awaited. Note that the 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 object. Returning a `Task` enables `WaitAndApologize` to be awaited. The 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 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"::: -## 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 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 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 or , 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 or 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 or 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 or 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\ @@ -69,7 +66,15 @@ Because and structure as a lightweight implementation of a generalized task-returning value. To use the type, you must add the `System.Threading.Tasks.Extensions` NuGet package to your project. The following example uses the 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\ + +Starting with C# 8.0, an async method may return an *async stream*, represented by . 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 diff --git a/docs/csharp/programming-guide/concepts/async/snippets/AsyncExamples.csproj b/docs/csharp/programming-guide/concepts/async/snippets/AsyncExamples.csproj new file mode 100644 index 0000000000000..d1a45393188c1 --- /dev/null +++ b/docs/csharp/programming-guide/concepts/async/snippets/AsyncExamples.csproj @@ -0,0 +1,10 @@ + + + + Exe + netcoreapp3.1 + Enable + AsyncExamples.Program + + + diff --git a/docs/csharp/programming-guide/concepts/async/snippets/AsyncStreams.cs b/docs/csharp/programming-guide/concepts/async/snippets/AsyncStreams.cs new file mode 100644 index 0000000000000..911544d0496aa --- /dev/null +++ b/docs/csharp/programming-guide/concepts/async/snippets/AsyncStreams.cs @@ -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); + } + + // + private static async IAsyncEnumerable 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(); + } + } + // + } +} \ No newline at end of file diff --git a/docs/csharp/programming-guide/concepts/async/snippets/Program.cs b/docs/csharp/programming-guide/concepts/async/snippets/Program.cs new file mode 100644 index 0000000000000..176fbc164081c --- /dev/null +++ b/docs/csharp/programming-guide/concepts/async/snippets/Program.cs @@ -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(); + } + } +} diff --git a/docs/csharp/programming-guide/concepts/async/snippets/async-returns1.cs b/docs/csharp/programming-guide/concepts/async/snippets/async-returns1.cs new file mode 100644 index 0000000000000..14b25ee018cd0 --- /dev/null +++ b/docs/csharp/programming-guide/concepts/async/snippets/async-returns1.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq; +using System.Threading.Tasks; + +public class FirstExample +{ + // + public static async Task ShowTodaysInfo() + { + string ret = $"Today is {DateTime.Today:D}\n" + + "Today's hours of leisure: " + + $"{await GetLeisureHours()}"; + return ret; + } + + static async Task GetLeisureHours() + { + // Task.FromResult is a placeholder for actual work that returns a string. + var today = await Task.FromResult(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 + // +} diff --git a/docs/csharp/programming-guide/concepts/async/snippets/async-returns1a.cs b/docs/csharp/programming-guide/concepts/async/snippets/async-returns1a.cs new file mode 100644 index 0000000000000..34e035efc747b --- /dev/null +++ b/docs/csharp/programming-guide/concepts/async/snippets/async-returns1a.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq; +using System.Threading.Tasks; + +public class SecondExample +{ + public static async Task ShowTodaysInfo() + { + // + 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; + // + } + + static async Task GetLeisureHours() + { + // Task.FromResult is a placeholder for actual work that returns a string. + var today = await Task.FromResult(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; + } +} diff --git a/docs/csharp/programming-guide/concepts/async/snippets/async-returns2.cs b/docs/csharp/programming-guide/concepts/async/snippets/async-returns2.cs new file mode 100644 index 0000000000000..2fa493a2cc9ca --- /dev/null +++ b/docs/csharp/programming-guide/concepts/async/snippets/async-returns2.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading.Tasks; + +public class ExampleTask +{ + // + 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. + // +} diff --git a/docs/csharp/programming-guide/concepts/async/snippets/async-returns2a.cs b/docs/csharp/programming-guide/concepts/async/snippets/async-returns2a.cs new file mode 100644 index 0000000000000..5a13169d886b9 --- /dev/null +++ b/docs/csharp/programming-guide/concepts/async/snippets/async-returns2a.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading.Tasks; + +public class AwaitTaskExample +{ + public 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"); + } +} +// 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. diff --git a/samples/snippets/csharp/programming-guide/async/async-returns3.cs b/docs/csharp/programming-guide/concepts/async/snippets/async-returns3.cs similarity index 82% rename from samples/snippets/csharp/programming-guide/async/async-returns3.cs rename to docs/csharp/programming-guide/concepts/async/snippets/async-returns3.cs index 277df9331cc53..f83db3f27cac7 100644 --- a/samples/snippets/csharp/programming-guide/async/async-returns3.cs +++ b/docs/csharp/programming-guide/concepts/async/snippets/async-returns3.cs @@ -3,7 +3,7 @@ public class NaiveButton { - public event EventHandler Clicked; + public event EventHandler? Clicked; public void Click() { @@ -15,9 +15,9 @@ public void Click() public class AsyncVoidExample { - static TaskCompletionSource tcs; + static TaskCompletionSource tcs = new TaskCompletionSource(); - static async Task Main() + public static async Task Main() { tcs = new TaskCompletionSource(); var secondHandlerFinished = tcs.Task; @@ -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(); @@ -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(); diff --git a/samples/snippets/csharp/programming-guide/async/async-valuetask.cs b/docs/csharp/programming-guide/concepts/async/snippets/async-valuetask.cs similarity index 96% rename from samples/snippets/csharp/programming-guide/async/async-valuetask.cs rename to docs/csharp/programming-guide/concepts/async/snippets/async-valuetask.cs index 96a042b8ea623..33799dae4a29d 100644 --- a/samples/snippets/csharp/programming-guide/async/async-valuetask.cs +++ b/docs/csharp/programming-guide/concepts/async/snippets/async-valuetask.cs @@ -3,7 +3,7 @@ class Program { - static Random rnd; + static Random? rnd; static void Main() { diff --git a/samples/snippets/csharp/programming-guide/async/async-returns1.cs b/samples/snippets/csharp/programming-guide/async/async-returns1.cs deleted file mode 100644 index ace54f3c9e28a..0000000000000 --- a/samples/snippets/csharp/programming-guide/async/async-returns1.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; - -public class Example -{ - public static void Main() - { - Console.WriteLine(ShowTodaysInfo().Result); - } - - private static async Task ShowTodaysInfo() - { - string ret = $"Today is {DateTime.Today:D}\n" + - "Today's hours of leisure: " + - $"{await GetLeisureHours()}"; - return ret; - } - - static async Task GetLeisureHours() - { - // Task.FromResult is a placeholder for actual work that returns a string. - var today = await Task.FromResult(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 -// diff --git a/samples/snippets/csharp/programming-guide/async/async-returns1a.cs b/samples/snippets/csharp/programming-guide/async/async-returns1a.cs deleted file mode 100644 index 5f701c8f557ed..0000000000000 --- a/samples/snippets/csharp/programming-guide/async/async-returns1a.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; - -public class Example -{ - public static void Main() - { - Console.WriteLine(ShowTodaysInfo().Result); - } - - private static async Task ShowTodaysInfo() - { - // - 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; - } - - static async Task GetLeisureHours() - { - // Task.FromResult is a placeholder for actual work that returns a string. - var today = await Task.FromResult(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 -// diff --git a/samples/snippets/csharp/programming-guide/async/async-returns2.cs b/samples/snippets/csharp/programming-guide/async/async-returns2.cs deleted file mode 100644 index a3ad3166706c9..0000000000000 --- a/samples/snippets/csharp/programming-guide/async/async-returns2.cs +++ /dev/null @@ -1,32 +0,0 @@ -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. diff --git a/samples/snippets/csharp/programming-guide/async/async-returns2a.cs b/samples/snippets/csharp/programming-guide/async/async-returns2a.cs deleted file mode 100644 index 4e67429bf843a..0000000000000 --- a/samples/snippets/csharp/programming-guide/async/async-returns2a.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Threading.Tasks; - -public class Example -{ - public static void Main() - { - DisplayCurrentInfo().Wait(); - } - - 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"); - } -} -// 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.