| Severity | Error |
| Reported by | PostSharp.Patterns.Threading |
| Message | The type of field "<Field>" is not compatible with the [<Aspect>] aspect: mutable struct types are not allowed. Make all fields of the struct read-only, add [ExplicitlySynchronized] to this field or property if access to it is synchronized manually in source code, use [AssumeImmutable], or register a TypeAdapter. |
Cause
A threading model checks the accesses to the fields of its class. A field of a struct type holds the struct itself, not a reference, so code can modify a field of the struct in place, for example this.position.Latitude = 0. PostSharp cannot check such modifications, so a threading model does not accept a field whose type is a mutable struct.
PostSharp considers a struct immutable when all its instance fields are read-only and their types are immutable, as in a readonly struct, or when the struct is marked as immutable with the AssumeImmutableAttribute custom attribute or with a registered type adapter. The primitive types, enumerations and the structs of the System namespace, such as DateTime and decimal, are immutable.
<Aspect> is the name of the class of the threading model aspect, for example SynchronizedAttribute.
In the following example, the Vehicle class has the SynchronizedAttribute threading model, and its position field is a Coordinates struct whose properties have setters.
using System;
using PostSharp.Patterns.Threading;
namespace Errors.THR018_Error;
public struct Coordinates
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
[Synchronized]
public class Vehicle
{
// PostSharp cannot check the modifications made inside a mutable struct.
private Coordinates position;
public void MoveTo( double latitude, double longitude )
{
this.position = new Coordinates { Latitude = latitude, Longitude = longitude };
}
public string Describe()
{
var current = this.position;
return $"Vehicle at {current.Latitude}, {current.Longitude}";
}
}
public static class Program
{
public static void Main()
{
var vehicle = new Vehicle();
vehicle.MoveTo( 50.08, 14.42 );
Console.WriteLine( vehicle.Describe() );
}
}
error THR018: THR018_Error.cs(17,25): The type of field "Errors.THR018_Error.Vehicle.position" is not compatible with the [SynchronizedAttribute] aspect: mutable struct types are not allowed. Make all fields of the struct read-only, add [ExplicitlySynchronized] to this field or property if access to it is synchronized manually in source code, use [AssumeImmutable], or register a TypeAdapter.
Resolution
- Make the struct immutable: declare it as a
readonly struct, or make all its fields read-only. Properties with aninitaccessor or with a getter only have read-only backing fields. To change the value, assign a new instance of the struct to the field. - When your code synchronizes the accesses to the field by itself, apply the ExplicitlySynchronizedAttribute custom attribute to the field, or to the automatic property.
- When a struct is immutable in practice, apply the AssumeImmutableAttribute custom attribute to it. For a struct of another assembly, apply the attribute to your assembly with the type of the struct as its argument, or register a TypeAdapter for it.
In the following example, Coordinates is a readonly struct.
using System;
using PostSharp.Patterns.Threading;
namespace Errors.THR018_Fixed;
// All fields of a read-only struct are read-only, so the struct is immutable.
public readonly struct Coordinates
{
public double Latitude { get; init; }
public double Longitude { get; init; }
}
[Synchronized]
public class Vehicle
{
private Coordinates position;
public void MoveTo( double latitude, double longitude )
{
this.position = new Coordinates { Latitude = latitude, Longitude = longitude };
}
public string Describe()
{
var current = this.position;
return $"Vehicle at {current.Latitude}, {current.Longitude}";
}
}
public static class Program
{
public static void Main()
{
var vehicle = new Vehicle();
vehicle.MoveTo( 50.08, 14.42 );
Console.WriteLine( vehicle.Describe() );
}
}
Vehicle at 50.08, 14.42
See Also
Reference
ExplicitlySynchronizedAttribute
AssumeImmutableAttribute
TypeAdapter
Other Resources
Writing Thread-Safe Code with Threading Models
Opting In and Out From Thread Safety