Open sandboxFocusImprove this doc

LAMA5164: The computed property reads a field that can't be observed

Severity Warning
Reported by Metalama.Patterns.Observability
Message The '<Field>' field cannot be observed: only private instance fields, constants, and read-only fields of immutable types are supported. Consider replacing the field with a property, or making it private.

Cause

The [Observable] aspect analyzes the getter of each computed property to find the fields and properties it depends on. It supports only the following fields:

  • private instance fields,
  • constants,
  • read-only fields of an immutable type, such as string, int, or a type marked as immutable. For details, see Metalama.Patterns.Immutability.

Metalama reports LAMA5164 when a computed property reads any other field, for example a public, protected, or internal field, or a mutable static field. Code of other classes can change such a field without raising a notification, so the computed property wouldn't be notified. In the message, <Field> is the field.

In the following example, Total reads the public field UnitPrice.

Source Code
1using Metalama.Patterns.Observability;
2

3namespace Doc.LAMA5164.Error;
4
5[Observable]
6public partial class OrderLine
7{
8    public decimal UnitPrice;
9













10    public int Quantity { get; set; }





11
12    // Warning: UnitPrice is a public field, so changes to it can't be observed.
















    Warning LAMA5164: The 'OrderLine.UnitPrice' field cannot be observed: only private instance fields, constants, and read-only fields of immutable types are supported. Consider replacing the field with a property, or making it private.

13    public decimal Total => this.UnitPrice * this.Quantity;
14}
15
Transformed Code
1using System.ComponentModel;
2using Metalama.Patterns.Observability;
3
4namespace Doc.LAMA5164.Error;
5
6[Observable]
7public partial class OrderLine : INotifyPropertyChanged
8{
9    private decimal _unitPrice;
10
11    public decimal UnitPrice
12    {
13        get
14        {
15            return _unitPrice;
16        }
17
18        set
19        {
20            if (_unitPrice != value)
21            {
22                _unitPrice = value;
23            }
24        }
25    }
26
27    private int _quantity;
28
29    public int Quantity
30    {
31        get
32        {
33            return _quantity;
34        }
35
36        set
37        {
38            if (_quantity != value)
39            {
40                _quantity = value;
41                OnPropertyChanged("Total");
42                OnPropertyChanged("Quantity");
43            }
44        }
45    }
46
47    // Warning: UnitPrice is a public field, so changes to it can't be observed.
48    public decimal Total => this.UnitPrice * this.Quantity;
49
50    protected virtual void OnPropertyChanged(string propertyName)
51    {
52        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
53    }
54
55    public event PropertyChangedEventHandler? PropertyChanged;
56}
57

Resolution

Do one of the following:

  • Replace the field with an auto-property, as in the following example. The aspect then raises a notification when the property changes.
  • Make the field private, and expose it through a property if other classes need it.
  • If the field never changes, make it readonly or const, provided its type is immutable.

Source Code
1using Metalama.Patterns.Observability;
2

3namespace Doc.LAMA5164.Fixed;
4
5[Observable]
6public partial class OrderLine
7{
8    // Fixed: the public field is replaced with an auto-property.
9    public decimal UnitPrice { get; set; }


10
11    public int Quantity { get; set; }


















12
13    public decimal Total => this.UnitPrice * this.Quantity;
















14}
15
Transformed Code
1using System.ComponentModel;
2using Metalama.Patterns.Observability;
3
4namespace Doc.LAMA5164.Fixed;
5
6[Observable]
7public partial class OrderLine : INotifyPropertyChanged
8{
9    private decimal _unitPrice;
10
11    // Fixed: the public field is replaced with an auto-property.
12    public decimal UnitPrice
13    {
14        get
15        {
16            return _unitPrice;
17        }
18
19        set
20        {
21            if (_unitPrice != value)
22            {
23                _unitPrice = value;
24                OnPropertyChanged("Total");
25                OnPropertyChanged("UnitPrice");
26            }
27        }
28    }
29
30    private int _quantity;
31
32    public int Quantity
33    {
34        get
35        {
36            return _quantity;
37        }
38
39        set
40        {
41            if (_quantity != value)
42            {
43                _quantity = value;
44                OnPropertyChanged("Total");
45                OnPropertyChanged("Quantity");
46            }
47        }
48    }
49
50    public decimal Total => this.UnitPrice * this.Quantity;
51
52    protected virtual void OnPropertyChanged(string propertyName)
53    {
54        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
55    }
56
57    public event PropertyChangedEventHandler? PropertyChanged;
58}
59

See also