| Severity | Error |
| Reported by | PostSharp.Patterns.Threading |
| Message | Field "<Field>" cannot be public or internal because its declaring class "<Class>" has a threading model that requires private fields. Make the field private, or apply the [ExplicitlySynchronized] custom attribute to this field to opt out from this rule. |
Cause
Several threading models protect the state of an object by controlling its entry points: the public and internal methods and property accessors acquire a lock, or are dispatched to the thread of the object, before they access the fields. A field that other classes can access directly bypasses this control, so these models require the fields of the class to be private.
PostSharp reports THR008 for a public, internal or protected internal field of a class that has one of the following threading models:
- SynchronizedAttribute;
- ReaderWriterSynchronizedAttribute;
- ActorAttribute;
- ThreadUnsafeAttribute, when run-time verification is enabled, which is the default in the debug configuration.
The ThreadAffineAttribute model does not require private fields. Protected fields are not reported.
In the following example, the Quantity field of the synchronized InventoryItem class is public.
using System;
using PostSharp.Patterns.Threading;
namespace Errors.THR008_Error;
[Synchronized]
public class InventoryItem
{
// Other threads could read and write this field without acquiring the lock of the object.
public int Quantity;
public void Restock( int count )
{
this.Quantity += count;
}
}
public static class Program
{
public static void Main()
{
var item = new InventoryItem();
item.Restock( 5 );
Console.WriteLine( $"Quantity: {item.Quantity}" );
}
}
error THR008: THR008_Error.cs(10,16): Field "Errors.THR008_Error.InventoryItem.Quantity" cannot be public or internal because its declaring class "Errors.THR008_Error.InventoryItem" has a threading model that requires private fields. Make the field private, or apply the [ExplicitlySynchronized] custom attribute to this field to opt out from this rule.
Resolution
- Make the field private, and expose it through a property or methods, whose accessors are entry points that the threading model controls. An automatic property is the shortest way, because its backing field is private.
- When your code synchronizes the access to the field by itself, apply the ExplicitlySynchronizedAttribute custom attribute to the field. PostSharp then neither reports the field nor checks the accesses to it.
In the following example, Quantity is an automatic property, whose accessors acquire the lock of the object.
using System;
using PostSharp.Patterns.Threading;
namespace Errors.THR008_Fixed;
[Synchronized]
public class InventoryItem
{
// The accessors of the property acquire the lock of the object; its backing field is private.
public int Quantity { get; private set; }
public void Restock( int count )
{
this.Quantity += count;
}
}
public static class Program
{
public static void Main()
{
var item = new InventoryItem();
item.Restock( 5 );
Console.WriteLine( $"Quantity: {item.Quantity}" );
}
}
Quantity: 5
See Also
Reference
SynchronizedAttribute
ReaderWriterSynchronizedAttribute
ActorAttribute
ExplicitlySynchronizedAttribute
Other Resources
Writing Thread-Safe Code with Threading Models
Opting In and Out From Thread Safety