Open sandboxFocusImprove this doc

THR020: Child Has No Threading Model

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

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