| Severity | Error |
| Reported by | PostSharp.Patterns.Model |
| Message | NotifyPropertyChangedAttribute: automatic analysis of property <Property> failed. Method <Method> contains direct access to field <Field>. Only fields of the current instance, constants and read-only fields of immutable types can be analyzed. To solve this error, make the field read-only if its value never changes, or expose the value as a property of an object that implements INotifyPropertyChanged. To ignore the dependency to the field <Field>, add the [SafeForDependencyAnalysis] attribute to the property <Property>, or specify the dependencies of the property explicitly with Depends.On. |
Cause
The NotifyPropertyChangedAttribute aspect analyzes the getter of each property at build time to find the fields and properties that its value depends on, so that it can raise the PropertyChanged event when one of them changes. The aspect detects the changes of the fields of the current instance only. It therefore reports DOM001 when the getter reads another field, because nothing would notify the property when that field changes:
- a static field that is not read-only, or a read-only static field of a mutable type;
- a field of another object, for example
this.customer.DiscountRate, even when that object is of the same class.
Constants and read-only fields of immutable types, such as int, string, decimal or an enum, are accepted, because their value cannot change.
<Method> is the method that reads the field: the getter itself, or a method of the same class that the getter calls, which the aspect analyzes too. PostSharp reports the message on the property.
In the following example, the Total property of Invoice reads the mutable static field TaxSettings.Rate.
using System;
using System.ComponentModel;
using PostSharp.Patterns.Model;
namespace Errors.DOM001_Error;
public static class TaxSettings
{
public static decimal Rate = 0.2m;
}
[NotifyPropertyChanged]
public class Invoice
{
public decimal Subtotal { get; set; }
// The getter reads a static field that any code can change, so the aspect cannot detect its changes.
public decimal Total => this.Subtotal * (1 + TaxSettings.Rate);
}
public static class Program
{
public static void Main()
{
Invoice invoice = new();
((INotifyPropertyChanged) invoice).PropertyChanged += ( _, e ) => Console.WriteLine( $"PropertyChanged: {e.PropertyName}" );
invoice.Subtotal = 100;
Console.WriteLine( $"Total: {invoice.Total}" );
}
}
error DOM001: DOM001_Error.cs(18,20): NotifyPropertyChangedAttribute: automatic analysis of property Errors.DOM001_Error.Invoice.Total failed. Method Errors.DOM001_Error.Invoice.get_Total contains direct access to field Errors.DOM001_Error.TaxSettings.Rate. Only fields of the current instance, constants and read-only fields of immutable types can be analyzed. To solve this error, make the field read-only if its value never changes, or expose the value as a property of an object that implements INotifyPropertyChanged. To ignore the dependency to the field Errors.DOM001_Error.TaxSettings.Rate, add the [SafeForDependencyAnalysis] attribute to the property Errors.DOM001_Error.Invoice.Total, or specify the dependencies of the property explicitly with Depends.On.
Resolution
- When the value of the field never changes, make the field
readonlyorconst, with an immutable type. - When the value is held by another object, expose it as a property of a class that implements
INotifyPropertyChanged, for example with the NotifyPropertyChangedAttribute aspect, and read it in the canonical formthis.field.Property. The aspect then subscribes to the changes of this object. - When you know the dependencies of the property better than the aspect does, add the SafeForDependencyAnalysisAttribute attribute to the property, and declare the dependencies that the aspect cannot find with On. The aspect then ignores the field, and does not notify the property when the field changes.
In the following example, TaxSettings.Rate is read-only, so the aspect accepts it, and notifies Total when Subtotal changes.
using System;
using System.ComponentModel;
using PostSharp.Patterns.Model;
namespace Errors.DOM001_Fixed;
public static class TaxSettings
{
// A read-only field of an immutable type cannot change, so the aspect does not need to track it.
public static readonly decimal Rate = 0.2m;
}
[NotifyPropertyChanged]
public class Invoice
{
public decimal Subtotal { get; set; }
public decimal Total => this.Subtotal * (1 + TaxSettings.Rate);
}
public static class Program
{
public static void Main()
{
Invoice invoice = new();
((INotifyPropertyChanged) invoice).PropertyChanged += ( _, e ) => Console.WriteLine( $"PropertyChanged: {e.PropertyName}" );
invoice.Subtotal = 100;
Console.WriteLine( $"Total: {invoice.Total}" );
}
}
PropertyChanged: Subtotal
PropertyChanged: Total
Total: 120.0
See Also
Reference
NotifyPropertyChangedAttribute
SafeForDependencyAnalysisAttribute
On
Other Resources
Handling Corner Cases of the NotifyPropertyChanged Aspect
Implementing INotifyPropertyChanged with Properties that Depend on Other Objects