Open sandboxFocusImprove this doc

COM009: Struct Child Is Read-Only

Severity Error
Reported by PostSharp.Patterns.Aggregation
Message The field or property "<Member>" cannot be read-only because it is a [Child] of a struct type. Remove the readonly modifier of the field, or add a setter to the automatic property.

Cause

A child of an AggregatableAttribute class can be a struct when the struct implements IAttachable. When PostSharp attaches the child to its parent, it calls AttachToParent, which stores the parent in the struct. A struct is a value, so attaching it changes the struct stored in the field, which a read-only field does not allow. PostSharp therefore reports COM009 when a field marked with ChildAttribute has a struct type and is readonly, or when such an automatic property has only a getter, whose backing field is read-only.

In the following example, the getter-only Size property of Order is a child of the struct type Dimensions.

using System;
using PostSharp.Patterns.Model;

namespace Errors.COM009_Error;

[Aggregatable]
public class Order
{
    // The backing field of a getter-only property is read-only.
    [Child]
    public Dimensions Size { get; } = new Dimensions( 30, 20 );
}

// A struct can be a child only when it implements IAttachable.
public struct Dimensions : IAttachable
{
    public Dimensions( int width, int height )
    {
        this.Width = width;
        this.Height = height;
        this.Parent = null;
    }

    public int Width { get; }

    public int Height { get; }

    public object? Parent { get; private set; }

    public RelationshipKind ParentRelationship => this.Parent == null ? RelationshipKind.None : RelationshipKind.Child;

    public event EventHandler? ParentChanged
    {
        add { }
        remove { }
    }

    public event EventHandler<AncestorChangedEventArgs>? AncestorChanged
    {
        add { }
        remove { }
    }

    public bool VisitChildren( ChildVisitor visitor, ChildVisitorOptions options = ChildVisitorOptions.None, object? state = null ) => true;

    public bool AttachToParent( RelationshipInfo relationshipInfo, IAttacher attacher, object attacherState )
    {
        this.Parent = attacher.GetNode( attacherState );

        return true;
    }

    public bool DetachFromParent()
    {
        this.Parent = null;

        return true;
    }

    public void OnDeserialization( object caller )
    {
    }
}

public static class Program
{
    public static void Main()
    {
        var order = new Order();
        Console.WriteLine( $"The size is attached to the order: {order.Size.Parent == order}" );
    }
}
error COM009: COM009_Error.cs(11,23): The field or property "Errors.COM009_Error.Order.Size" cannot be read-only because it is a [Child] of a struct type. Remove the readonly modifier of the field, or add a setter to the automatic property.

Resolution

  • For an automatic property, add a setter, which can be private.
  • For a field, remove the readonly modifier.

In the following example, Size has a private setter, so the struct is attached to the order.

using System;
using PostSharp.Patterns.Model;

namespace Errors.COM009_Fixed;

[Aggregatable]
public class Order
{
    // With a setter, the backing field is not read-only.
    [Child]
    public Dimensions Size { get; private set; } = new Dimensions( 30, 20 );
}

// A struct can be a child only when it implements IAttachable.
public struct Dimensions : IAttachable
{
    public Dimensions( int width, int height )
    {
        this.Width = width;
        this.Height = height;
        this.Parent = null;
    }

    public int Width { get; }

    public int Height { get; }

    public object? Parent { get; private set; }

    public RelationshipKind ParentRelationship => this.Parent == null ? RelationshipKind.None : RelationshipKind.Child;

    public event EventHandler? ParentChanged
    {
        add { }
        remove { }
    }

    public event EventHandler<AncestorChangedEventArgs>? AncestorChanged
    {
        add { }
        remove { }
    }

    public bool VisitChildren( ChildVisitor visitor, ChildVisitorOptions options = ChildVisitorOptions.None, object? state = null ) => true;

    public bool AttachToParent( RelationshipInfo relationshipInfo, IAttacher attacher, object attacherState )
    {
        this.Parent = attacher.GetNode( attacherState );

        return true;
    }

    public bool DetachFromParent()
    {
        this.Parent = null;

        return true;
    }

    public void OnDeserialization( object caller )
    {
    }
}

public static class Program
{
    public static void Main()
    {
        var order = new Order();
        Console.WriteLine( $"The size is attached to the order: {order.Size.Parent == order}" );
    }
}
The size is attached to the order: True

See Also

Reference

AggregatableAttribute
ChildAttribute
IAttachable

Other Resources

Annotating an Object Model for Parent/Child Relationships (Aggregatable)