| 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.
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
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