| Severity | Error |
| Reported by | PostSharp.Patterns.Threading |
| Message | Method "<Method>" cannot be enhanced with the [Dispatched] aspect in non-blocking mode. When the ExecutionMode property is NonBlocking or NonBlockingContextSwitch, for example with [Dispatched(true)], this aspect can only be applied 1) to void methods with no ref, out or in parameters; 2) to async methods with return type void, Task or Task<T>. |
Cause
The DispatchedAttribute aspect executes a method in the thread of the object, typically the UI thread. In non-blocking mode, the caller does not wait for the method: it enqueues the call and continues immediately. The aspect is in non-blocking mode when you pass true to its constructor, or when you set its ExecutionMode property to NonBlocking or NonBlockingContextSwitch.
Because the caller does not wait, it cannot receive a return value or the values of ref and out parameters. PostSharp therefore reports THR001 when a method dispatched in non-blocking mode is:
- a method without the
asynckeyword whose return type is notvoid, including a method that returns aTaskwithout beingasync; - an
asyncmethod whose return type is neithervoidnorTaskorTask<T>, for exampleValueTask<T>; - a method with a
ref,outorinparameter.
In the following example, the AddOrder method returns the number of orders, but the aspect is in non-blocking mode.
using System;
using System.Collections.Generic;
using System.Threading;
using PostSharp.Patterns.Threading;
namespace Errors.THR001_Error;
public class OrderListView
{
private readonly List<string> orders = new();
// In non-blocking mode, the caller does not wait for the return value.
[Dispatched( true )]
public int AddOrder( string order )
{
this.orders.Add( order );
return this.orders.Count;
}
}
public static class Program
{
public static void Main()
{
// A console application has no UI thread, so the view uses the synchronization context of the main thread.
SynchronizationContext.SetSynchronizationContext( new SynchronizationContext() );
var view = new OrderListView();
Console.WriteLine( $"Orders in the view: {view.AddOrder( "Order 1" )}" );
Console.WriteLine( $"Orders in the view: {view.AddOrder( "Order 2" )}" );
}
}
error THR001: THR001_Error.cs(14,16): Method "Errors.THR001_Error.OrderListView.AddOrder(System.String)" cannot be enhanced with the [Dispatched] aspect in non-blocking mode. When the ExecutionMode property is NonBlocking or NonBlockingContextSwitch, for example with [Dispatched(true)], this aspect can only be applied 1) to void methods with no ref, out or in parameters; 2) to async methods with return type void, Task or Task<T>.
Resolution
- When the caller needs the result of the method, use the blocking mode, which is the default: remove the
trueargument, or set ExecutionMode to Blocking. The caller then waits for the method to complete in the thread of the object. - When the caller should not wait, make the method
asyncand return aTaskor aTask<T>, or returnvoidand remove theref,outandinparameters.
In the following example, the aspect is in blocking mode, so the caller receives the number of orders.
using System;
using System.Collections.Generic;
using System.Threading;
using PostSharp.Patterns.Threading;
namespace Errors.THR001_Fixed;
public class OrderListView
{
private readonly List<string> orders = new();
// In blocking mode, the caller waits for the method to complete and receives its return value.
[Dispatched]
public int AddOrder( string order )
{
this.orders.Add( order );
return this.orders.Count;
}
}
public static class Program
{
public static void Main()
{
// A console application has no UI thread, so the view uses the synchronization context of the main thread.
SynchronizationContext.SetSynchronizationContext( new SynchronizationContext() );
var view = new OrderListView();
Console.WriteLine( $"Orders in the view: {view.AddOrder( "Order 1" )}" );
Console.WriteLine( $"Orders in the view: {view.AddOrder( "Order 2" )}" );
}
}
Orders in the view: 1
Orders in the view: 2
See Also
Reference
DispatchedAttribute
DispatchedExecutionMode
Other Resources