| Severity | Error |
| Reported by | PostSharp.Patterns.Threading |
| Message | The type of field <Field> is not compatible with the [<Aspect>] aspect: the type is not immutable and has no threading model. Consider adding a threading model to the type, using [AssumeImmutable], or registering a TypeAdapter for this type.<Note> |
Cause
An object with a threading model, such as SynchronizedAttribute or ActorAttribute, also protects its children, the objects that it references from fields annotated with ChildAttribute. It can protect a child only when the type of the child is immutable or has a threading model of its own: the methods of a plain class do not take the lock of the parent and are not verified, so another thread could modify the child while the parent holds its lock.
PostSharp reports THR020 when, after all aspects have been discovered, the type of a child field is not immutable and has no threading model. <Aspect> is the name of the aspect class of the parent. <Note> is usually empty; it explains that an [ExplicitlySynchronizedAttribute] attribute applied to the backing field of an automatic property has no effect, when that is the case. When the type of the field is a collection that PostSharp cannot enhance, such as List<T>, PostSharp reports COM008 instead.
In the following example, the synchronized Customer class has a child of the Address class, which has no threading model.
using System;
using PostSharp.Patterns.Model;
using PostSharp.Patterns.Threading;
namespace Errors.THR020_Error;
[Synchronized]
public class Customer
{
// The Address class has no threading model, so the lock of the customer does not protect it.
[Child]
private readonly Address address = new();
public string City => this.address.City;
public void Move( string city )
{
this.address.City = city;
}
}
public class Address
{
public string City { get; set; } = "Prague";
}
public static class Program
{
public static void Main()
{
var customer = new Customer();
customer.Move( "Brno" );
Console.WriteLine( $"The customer lives in {customer.City}." );
}
}
error THR020: THR020_Error.cs(12,30): The type of field Errors.THR020_Error.Customer.address is not compatible with the [SynchronizedAttribute] aspect: the type is not immutable and has no threading model. Consider adding a threading model to the type, using [AssumeImmutable], or registering a TypeAdapter for this type.
Resolution
- Add a threading model to the type of the child that is compatible with the model of the parent, as listed in Compatibility of Threading Models.
- When the type is immutable but PostSharp cannot prove it, apply AssumeImmutableAttribute to the type, or to the assembly for a type of another assembly.
- When the field references an object that the parent does not own, annotate it with ReferenceAttribute instead of ChildAttribute.
- When you synchronize the access to the field yourself, annotate the field or its property with ExplicitlySynchronizedAttribute.
In the following example, the Address class is synchronized, so it shares the lock of its customer.
using System;
using PostSharp.Patterns.Model;
using PostSharp.Patterns.Threading;
namespace Errors.THR020_Fixed;
[Synchronized]
public class Customer
{
[Child]
private readonly Address address = new();
public string City => this.address.City;
public void Move( string city )
{
this.address.City = city;
}
}
// The Address class now has a threading model: as a child, it shares the lock of the customer.
[Synchronized]
public class Address
{
public string City { get; set; } = "Prague";
}
public static class Program
{
public static void Main()
{
var customer = new Customer();
customer.Move( "Brno" );
Console.WriteLine( $"The customer lives in {customer.City}." );
}
}
The customer lives in Brno.
See Also
Reference
ChildAttribute
AssumeImmutableAttribute
ExplicitlySynchronizedAttribute
Other Resources
Compatibility of Threading Models
Opting In and Out From Thread Safety