2023-05-03 11:06:20 +02:00
== Why is this an issue?
2023-07-04 16:21:06 +02:00
An https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/async[`async`] method with a `void` return type does not follow the https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/task-asynchronous-programming-model[task asynchronous programming (TAP)] model since the return type should be https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task[`Task`] or https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1[`Task<TResult>`]
2021-04-28 16:49:39 +02:00
2023-07-04 16:21:06 +02:00
Doing so prevents control over the https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/async-scenarios[asynchronous execution], such as:
2021-04-28 16:49:39 +02:00
2023-07-04 16:21:06 +02:00
* waiting for the execution to complete
* catching any exception that might occur during execution
* testing execution behavior
2021-04-28 16:49:39 +02:00
2023-07-04 16:21:06 +02:00
=== Exceptions
2021-04-28 16:49:39 +02:00
2023-07-04 16:21:06 +02:00
* Methods with the https://learn.microsoft.com/en-us/dotnet/api/system.eventhandler[`EventHandler`] delegate signature.
+
Using `void` for `EventHandler` is compliant with the TAP model.
+
[source,csharp]
----
public async void button1_Click(object sender, EventArgs e)
{
await DoSomethingAsync();
}
----
2023-10-30 10:33:56 +01:00
* Methods name matching ``++On[A-Z]\w*++`` pattern.
2023-07-04 16:21:06 +02:00
+
Some frameworks may not use the same `EventHandler` method signature
+
[source,csharp]
----
public async void OnClick(EventContext data)
{
await DoSomethingAsync();
}
----
2021-04-28 16:49:39 +02:00
2023-07-04 16:21:06 +02:00
== How to fix it
2021-04-28 18:08:03 +02:00
2023-07-04 16:21:06 +02:00
Update the return type of the method from `void` to `Task`.
2021-04-28 16:49:39 +02:00
2023-07-04 16:21:06 +02:00
=== Code examples
==== Noncompliant code example
[source,csharp,diff-id=1,diff-type=noncompliant]
2021-04-28 16:49:39 +02:00
----
2023-07-04 16:21:06 +02:00
private async void ThrowExceptionAsync() // Noncompliant: async method return type is 'void'
2021-04-28 16:49:39 +02:00
{
2023-07-04 16:21:06 +02:00
throw new InvalidOperationException();
}
2021-04-28 16:49:39 +02:00
2023-07-04 16:21:06 +02:00
public void Method()
{
try
2021-04-28 16:49:39 +02:00
{
2023-07-04 16:21:06 +02:00
ThrowExceptionAsync();
2021-04-28 16:49:39 +02:00
}
2023-07-04 16:21:06 +02:00
catch (Exception)
2021-04-28 16:49:39 +02:00
{
2023-07-04 16:21:06 +02:00
// The exception is never caught here
throw;
2021-04-28 16:49:39 +02:00
}
}
----
2021-04-28 18:08:03 +02:00
2023-07-04 16:21:06 +02:00
==== Compliant solution
2021-04-28 16:49:39 +02:00
2023-07-04 16:21:06 +02:00
[source,csharp,diff-id=1,diff-type=compliant]
2021-04-28 16:49:39 +02:00
----
2023-07-04 16:21:06 +02:00
private async Task ThrowExceptionAsync() // Compliant: async method return type is 'Task'
2021-04-28 16:49:39 +02:00
{
2023-07-04 16:21:06 +02:00
throw new InvalidOperationException();
}
2021-04-28 16:49:39 +02:00
2024-07-23 15:21:02 +02:00
public async Task Method()
2023-07-04 16:21:06 +02:00
{
try
2021-04-28 16:49:39 +02:00
{
2023-07-04 16:21:06 +02:00
await ThrowExceptionAsync();
2021-04-28 16:49:39 +02:00
}
2023-07-04 16:21:06 +02:00
catch (Exception)
2021-04-28 16:49:39 +02:00
{
2023-07-04 16:21:06 +02:00
// The exception is caught here
throw;
2021-04-28 16:49:39 +02:00
}
}
----
2023-07-04 16:21:06 +02:00
== Resources
2021-04-28 18:08:03 +02:00
2023-07-04 16:21:06 +02:00
=== Documentation
2021-04-28 16:49:39 +02:00
2023-07-04 16:21:06 +02:00
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/async[`async` (C# Reference)]
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/async-scenarios[Asynchronous programming]
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/task-asynchronous-programming-model[Task asynchronous programming model]
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task[`Task` Class]
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1[`Task<TResult>` Class]
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/api/system.eventhandler[`EventHandler` Delegate]
2021-04-28 18:08:03 +02:00
2021-06-02 20:44:38 +02:00
2023-10-18 11:43:40 +02:00
ifdef::env-github,rspecator-view[]
2021-09-20 15:38:42 +02:00
'''
== Implementation Specification
(visible only on this page)
2023-05-25 14:18:12 +02:00
=== Message
Return "Task" instead.
2021-09-20 15:38:42 +02:00
2021-06-08 15:52:13 +02:00
'''
2021-06-02 20:44:38 +02:00
== Comments And Links
(visible only on this page)
2023-05-25 14:18:12 +02:00
=== on 30 Jun 2015, 13:36:13 Ann Campbell wrote:
2023-07-04 16:21:06 +02:00
\[~tamas.vajk] I don't understand the code snippets. The `async Task` method doesn't return anything.
2023-05-25 14:18:12 +02:00
Also, could you morph the Noncompliant Example into a Compliant Solution, please?
=== on 1 Jul 2015, 07:10:48 Tamas Vajk wrote:
\[~ann.campbell.2] I added the compliant solution.
2023-07-04 16:21:06 +02:00
A method with `async` keyword returning a `Task` is like a non `async` method with `void` return type. Similarly in an `async Task<int>` method we can return a simple `int`. (\https://msdn.microsoft.com/en-us/library/hh524395.aspx)
2023-05-25 14:18:12 +02:00
=== on 1 Jul 2015, 11:31:53 Ann Campbell wrote:
2023-07-04 16:21:06 +02:00
\[~tamas.vajk] that makes me wonder if we should generalize this rule to catch _any_ `async` method that does not return a `Task`...?
2023-05-25 14:18:12 +02:00
=== on 1 Jul 2015, 11:49:54 Tamas Vajk wrote:
2023-07-04 16:21:06 +02:00
\[~ann.campbell.2] That's a compiler error (CS1983, _The return type of async must be void, Task or Task<T>_).
2023-05-25 14:18:12 +02:00
=== on 1 Jul 2015, 11:59:39 Ann Campbell wrote:
Okay, thanks [~tamas.vajk]
2023-10-18 11:43:40 +02:00
endif::env-github,rspecator-view[]