| Severity | Error |
| Reported by | PostSharp.Patterns.Threading |
| Message | Method "<Method>" cannot have ref, out or in parameters because its declaring class has the [Actor] threading model and the method can be invoked from outside the object. Return the value through a Task<T> instead. |
Cause
The ActorAttribute aspect does not execute a call to an actor in the thread of the caller: it adds the call to the message queue of the actor, which executes the calls one after the other. A by-reference parameter refers to a variable of the caller, which the actor would read or write later, from another thread. PostSharp therefore reports THR009 for a method of an actor that has a ref, out or in parameter and is an entry point of the object, that is, a method that can be invoked from outside the object:
- a
public,internalorprotected internalmethod; - a method that implements an interface;
- a protected method that overrides a method of a base class without a threading model;
- a method with the EntryPointAttribute custom attribute.
Private methods are not dispatched, because only the actor calls them, so they can have by-reference parameters.
In the following example, the GetReading method of the TemperatureSensor actor returns its value through an out parameter. PostSharp reports the message without a location.
using System;
using PostSharp.Patterns.Threading;
namespace Errors.THR009_Error;
[Actor]
public class TemperatureSensor
{
private double lastReading = 21.5;
// The caller does not wait for the actor, so it cannot receive the value of an out parameter.
public void GetReading( out double reading )
{
reading = this.lastReading;
}
}
public static class Program
{
public static void Main()
{
var sensor = new TemperatureSensor();
sensor.GetReading( out var reading );
Console.WriteLine( $"Temperature: {reading}" );
}
}
error THR009: THR009_Error.cs(12,17): Method "Errors.THR009_Error.TemperatureSensor.GetReading(System.Double@)" cannot have ref, out or in parameters because its declaring class has the [Actor] threading model and the method can be invoked from outside the object. Return the value through a Task<T> instead.
Resolution
- To return a value, make the method
asyncand return aTask<T>, which the caller awaits. In an actor, anasyncmethod must also have the ReentrantAttribute custom attribute; otherwise, PostSharp reports THR017. - To pass several values to the actor or receive several values from it, group them in a type instead of using by-reference parameters.
- When the method does not need to be an entry point of the actor, make it private.
In the following example, GetReadingAsync returns the reading through a Task<double>.
using System;
using System.Threading.Tasks;
using PostSharp.Patterns.Threading;
namespace Errors.THR009_Fixed;
[Actor]
public class TemperatureSensor
{
private double lastReading = 21.5;
// The actor returns the value through a task, which the caller awaits.
[Reentrant]
public async Task<double> GetReadingAsync()
{
await Task.Yield();
return this.lastReading;
}
}
public static class Program
{
public static async Task Main()
{
var sensor = new TemperatureSensor();
var reading = await sensor.GetReadingAsync();
Console.WriteLine( $"Temperature: {reading}" );
}
}
Temperature: 21.5
See Also
Reference
ActorAttribute
ReentrantAttribute
EntryPointAttribute
Other Resources