| Severity | Error |
| Reported by | PostSharp.Patterns.Threading |
| Message | The type of field <Field> is not compatible with the [<Aspect>] aspect: objects with threading model "<ChildModel>" cannot be children of objects with threading model "<ParentModel>". |
Cause
An object with a threading model, such as FreezableAttribute or SynchronizedAttribute, also protects its children, the objects that it references from fields annotated with ChildAttribute. A child either shares the concurrency controller of its parent, such as its lock, or keeps its own. Some combinations of models cannot work together: for example, a synchronized object cannot be frozen, and an actor does not take the locks that a synchronized child needs. The article Compatibility of Threading Models lists which models can be children of which.
PostSharp reports THR019 when the type of a child field has a threading model that cannot be a child of the threading model of the class. <Aspect> is the name of the aspect class of the parent, <ChildModel> is the threading model of the type of the field, and <ParentModel> is the threading model of the class.
In the following example, the freezable Invoice class has a child whose class is synchronized.
using System;
using PostSharp.Patterns.Model;
using PostSharp.Patterns.Threading;
namespace Errors.THR019_Error;
[Freezable]
public class Invoice
{
// A freezable object cannot have a synchronized child.
[Child]
private readonly InvoiceLine line;
public Invoice( string product, int quantity )
{
this.line = new InvoiceLine { Product = product, Quantity = quantity };
}
public InvoiceLine Line => this.line;
}
[Synchronized]
public class InvoiceLine
{
public string Product { get; set; } = "";
public int Quantity { get; set; }
}
public static class Program
{
public static void Main()
{
var invoice = new Invoice( "Keyboard", 2 );
((IFreezable) invoice).Freeze();
try
{
invoice.Line.Quantity = 3;
}
catch ( ObjectReadOnlyException )
{
Console.WriteLine( $"The line of {invoice.Line.Product} is frozen with its invoice." );
}
}
}
error THR019: THR019_Error.cs(12,34): The type of field Errors.THR019_Error.Invoice.line is not compatible with the [FreezableAttribute] aspect: objects with threading model "Synchronized" cannot be children of objects with threading model "Freezable".
Resolution
- Give the type of the child a threading model that is compatible with the model of the parent, as listed in Compatibility of Threading Models. For a freezable or immutable parent, the child must be freezable or immutable.
- When the field references an object that the parent does not own, annotate it with ReferenceAttribute instead of ChildAttribute.
In the following example, the InvoiceLine class is freezable, so it is frozen with its invoice.
using System;
using PostSharp.Patterns.Model;
using PostSharp.Patterns.Threading;
namespace Errors.THR019_Fixed;
[Freezable]
public class Invoice
{
[Child]
private readonly InvoiceLine line;
public Invoice( string product, int quantity )
{
this.line = new InvoiceLine { Product = product, Quantity = quantity };
}
public InvoiceLine Line => this.line;
}
// A freezable child is compatible with a freezable parent, and is frozen with it.
[Freezable]
public class InvoiceLine
{
public string Product { get; set; } = "";
public int Quantity { get; set; }
}
public static class Program
{
public static void Main()
{
var invoice = new Invoice( "Keyboard", 2 );
((IFreezable) invoice).Freeze();
try
{
invoice.Line.Quantity = 3;
}
catch ( ObjectReadOnlyException )
{
Console.WriteLine( $"The line of {invoice.Line.Product} is frozen with its invoice." );
}
}
}
The line of Keyboard is frozen with its invoice.
See Also
Reference
ChildAttribute
FreezableAttribute
SynchronizedAttribute
Other Resources
Compatibility of Threading Models
Writing Thread-Safe Code with Threading Models