Open sandboxFocusImprove this doc

LAMA5152: A field or property has a struct type that implements INotifyPropertyChanged

Severity Error
Reported by Metalama.Patterns.Observability
Message The type '<Type>' of the <Kind> '<FieldOrProperty>' is a struct that implements INotifyPropertyChanged. The [Observable] aspect does not support structs that implement INotifyPropertyChanged.

Cause

When a field or property of an observable class has a type that implements INotifyPropertyChanged, the [Observable] aspect treats its value as a child object: it can subscribe to the PropertyChanged event of the value to propagate the changes of its properties. This mechanism relies on reference semantics and can't work with a struct, because every read of the field or property returns a copy of the value.

Metalama reports LAMA5152 on each field or property processed by the aspect whose type is a value type that implements INotifyPropertyChanged, including a type parameter constrained to struct and INotifyPropertyChanged. In the message, <Kind> is the kind of declaration, <FieldOrProperty> is the field or property, and <Type> is its type.

In the following example, the Position property has the type Coordinates, a struct that implements INotifyPropertyChanged.

1using Metalama.Patterns.Observability;
2using System.ComponentModel;
3
4namespace Doc.LAMA5152.Error;
5
6public struct Coordinates : INotifyPropertyChanged
7{
8    private double _latitude;
9
10    public double Latitude
11    {
12        get => this._latitude;
13        set
14        {
15            this._latitude = value;
16            this.PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( nameof(this.Latitude) ) );
17        }
18    }
19
20    public event PropertyChangedEventHandler? PropertyChanged;
21}
22
23[Observable]
24public partial class Vehicle
25{
26    // Error: Coordinates is a struct that implements INotifyPropertyChanged.
    Error LAMA5152: The type 'Coordinates' of the property 'Vehicle.Position' is a struct that implements INotifyPropertyChanged. The [Observable] aspect does not support structs that implement INotifyPropertyChanged.

27    public Coordinates Position { get; set; }
28}
29

Resolution

Do one of the following:

  • Make the struct immutable and remove its implementation of INotifyPropertyChanged. To change the value, assign a new instance to the property: the aspect then raises the notification for the property itself. The following example uses a readonly record struct.
  • Change the struct into a class, so that the aspect can observe its changes as a child object.
  • If the property doesn't need change notifications, exclude it from the aspect with the [NotObservable] attribute.

Source Code
1using Metalama.Patterns.Observability;
2

3namespace Doc.LAMA5152.Fixed;
4
5// Fixed: Coordinates is an immutable struct that doesn't implement INotifyPropertyChanged.
6public readonly record struct Coordinates( double Latitude );
7
8[Observable]
9public partial class Vehicle
10{
11    public Coordinates Position { get; set; }
12}


13
Transformed Code
1using System.ComponentModel;
2using Metalama.Patterns.Observability;
3
4namespace Doc.LAMA5152.Fixed;
5
6// Fixed: Coordinates is an immutable struct that doesn't implement INotifyPropertyChanged.
7public readonly record struct Coordinates(double Latitude);
8
9[Observable]
10public partial class Vehicle : INotifyPropertyChanged
11{
12    private Coordinates _position;
13
14    public Coordinates Position
15    {
16        get
17        {
18            return _position;
19        }
20
21        set
22        {
23            if (_position != value)
24            {
25                _position = value;
26                OnPropertyChanged("Position");
27            }
28        }
29    }
30
31    protected virtual void OnPropertyChanged(string propertyName)
32    {
33        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
34    }
35
36    public event PropertyChangedEventHandler? PropertyChanged;
37}
38

See also