| Severity | Error |
| Reported by | PostSharp.Patterns.Model |
| Message | The type of field <Field> is not compatible with the [Recordable] aspect: mutable value types are not allowed. Make the type a readonly struct, or add the [NotRecorded] attribute to the field. |
Warning
Undo/Redo was deprecated in PostSharp 2026.0 and will not be maintained. The API will be removed in the future.
Cause
The RecordableAttribute aspect records each assignment of a field of the class, so that the assignment can be undone. When the field is of a value type whose own fields can change, code can change the value in place, for example with this.position.X = 5, which the aspect cannot record.
PostSharp reports DOM021 on a field of the class when its type is a struct that is neither a primitive type, a type of the System namespace, nor immutable. A struct is immutable when it is a readonly struct, when all its fields are read-only and of immutable types, or when it has the AssumeImmutableAttribute attribute. Fields of delegate types, and fields with the NotRecordedAttribute attribute, are not checked.
In the following example, the Shape class has a field of the Point struct, whose fields are not read-only.
using System;
using PostSharp.Patterns.Recording;
// The Recordable aspect is deprecated.
#pragma warning disable CS0618
namespace Errors.DOM021_Error;
public struct Point
{
public int X;
public int Y;
public Point( int x, int y )
{
this.X = x;
this.Y = y;
}
public override string ToString() => $"({this.X}, {this.Y})";
}
[Recordable]
public class Shape
{
// The fields of Point can be changed in place, so the aspect cannot record the changes.
private Point position;
public Point Position => this.position;
public void MoveTo( int x, int y )
{
this.position = new Point( x, y );
}
}
public static class Program
{
public static void Main()
{
Shape shape = new Shape();
shape.MoveTo( 10, 20 );
shape.MoveTo( 30, 40 );
RecordingServices.DefaultRecorder.Undo();
Console.WriteLine( $"After undo: {shape.Position}" );
}
}
error DOM021: DOM021_Error.cs(27,19): The type of field Errors.DOM021_Error.Shape.position is not compatible with the [Recordable] aspect: mutable value types are not allowed. Make the type a readonly struct, or add the [NotRecorded] attribute to the field.
Resolution
- Make the struct immutable, for example a
readonly struct. Code then changes the field by assigning a new value, which the aspect records. - When the changes of the field do not need to be undone, add the NotRecordedAttribute attribute to the field.
In the following example, Point is a readonly struct, and undoing the last operation restores the previous position.
using System;
using PostSharp.Patterns.Recording;
// The Recordable aspect is deprecated.
#pragma warning disable CS0618
namespace Errors.DOM021_Fixed;
// A readonly struct cannot be changed in place: the aspect records the replacement of the whole value.
public readonly struct Point
{
public readonly int X;
public readonly int Y;
public Point( int x, int y )
{
this.X = x;
this.Y = y;
}
public override string ToString() => $"({this.X}, {this.Y})";
}
[Recordable]
public class Shape
{
private Point position;
public Point Position => this.position;
public void MoveTo( int x, int y )
{
this.position = new Point( x, y );
}
}
public static class Program
{
public static void Main()
{
Shape shape = new Shape();
shape.MoveTo( 10, 20 );
shape.MoveTo( 30, 40 );
RecordingServices.DefaultRecorder.Undo();
Console.WriteLine( $"After undo: {shape.Position}" );
}
}
After undo: (10, 20)
See Also
Reference
RecordableAttribute
NotRecordedAttribute
Other Resources