Open sandboxFocusImprove this doc

DOM011: Property Getter Calls a Virtual Method

Severity Error
Reported by PostSharp.Patterns.Model
Message NotifyPropertyChangedAttribute: automatic analysis of property <Property> failed. Method <Method> contains a call to virtual method <VirtualMethod>. Calls to virtual methods cannot be analyzed. To fix this error, remove the calls to virtual methods. To ignore this error, add the [SafeForDependencyAnalysis] attribute to property <Property>.

Cause

The NotifyPropertyChangedAttribute aspect finds the fields and properties on which each property depends by analyzing the code of its getter at build time, and of the methods that the getter calls. It cannot analyze a call to a virtual, abstract or interface method, because the method that runs is chosen at run time, and an override in another class or assembly can read other fields.

PostSharp reports DOM011 on the property when its getter, or a method of the class that the getter calls, calls such a method and uses its return value. <Method> is the method that contains the call: the getter itself, or one of the methods that it calls. Calls to methods that return void, to ToString and GetHashCode, and to methods that PostSharp knows to have no side effect, are not reported.

In the following example, the getter of Total calls the virtual ComputeDiscount method.

using System;
using System.ComponentModel;
using PostSharp.Patterns.Model;

namespace Errors.DOM011_Error;

[NotifyPropertyChanged]
public class Order
{
    public decimal Subtotal { get; set; }

    public decimal DiscountRate { get; set; }

    // The getter calls a virtual method, whose overrides the aspect cannot analyze.
    public decimal Total => this.Subtotal - this.ComputeDiscount();

    protected virtual decimal ComputeDiscount() => this.Subtotal * this.DiscountRate;
}

public static class Program
{
    public static void Main()
    {
        Order order = new Order();
        ( (INotifyPropertyChanged) order ).PropertyChanged += ( _, e ) => Console.WriteLine( $"{e.PropertyName} changed." );
        order.Subtotal = 100;
        order.DiscountRate = 0.1m;
        Console.WriteLine( $"Total: {order.Total}" );
    }
}
error DOM011: DOM011_Error.cs(15,20): NotifyPropertyChangedAttribute: automatic analysis of property Errors.DOM011_Error.Order.Total failed. Method Errors.DOM011_Error.Order.get_Total contains a call to virtual method Errors.DOM011_Error.Order.ComputeDiscount. Calls to virtual methods cannot be analyzed. To fix this error, remove the calls to virtual methods. To ignore this error, add the [SafeForDependencyAnalysis] attribute to property Errors.DOM011_Error.Order.Total.

Resolution

  • When the method does not need to be virtual, make it non-virtual, so that the aspect analyzes its code.
  • Otherwise, add the SafeForDependencyAnalysisAttribute attribute to the property. The aspect then skips the calls that it cannot analyze, but still finds the dependencies that the getter reads directly. Declare the dependencies of the virtual method with On in the getter; without them, the property is not notified when these dependencies change.

In the following example, Total has the SafeForDependencyAnalysisAttribute attribute and declares the properties that ComputeDiscount reads. The aspect notifies Total when each of them changes.

using System;
using System.ComponentModel;
using PostSharp.Patterns.Model;

namespace Errors.DOM011_Fixed;

[NotifyPropertyChanged]
public class Order
{
    public decimal Subtotal { get; set; }

    public decimal DiscountRate { get; set; }

    // The aspect skips the virtual call, and Depends.On declares what the virtual method reads.
    [SafeForDependencyAnalysis]
    public decimal Total
    {
        get
        {
            Depends.On( this.Subtotal, this.DiscountRate );

            return this.Subtotal - this.ComputeDiscount();
        }
    }

    protected virtual decimal ComputeDiscount() => this.Subtotal * this.DiscountRate;
}

public static class Program
{
    public static void Main()
    {
        Order order = new Order();
        ( (INotifyPropertyChanged) order ).PropertyChanged += ( _, e ) => Console.WriteLine( $"{e.PropertyName} changed." );
        order.Subtotal = 100;
        order.DiscountRate = 0.1m;
        Console.WriteLine( $"Total: {order.Total}" );
    }
}
Subtotal changed.
Total changed.
DiscountRate changed.
Total changed.
Total: 90.0

See Also

Reference

NotifyPropertyChangedAttribute
SafeForDependencyAnalysisAttribute
On

Other Resources

Handling Corner Cases of the NotifyPropertyChanged Aspect
Understanding the NotifyPropertyChanged Aspect