Open sandboxFocusImprove this doc

DOM003: Property Getter Creates a Delegate

Severity Error
Reported by PostSharp.Patterns.Model
Message NotifyPropertyChangedAttribute: automatic analysis of property <Property> failed. Method <Method> creates a delegate, which cannot be analyzed. To solve this error, create the delegate outside of the code that the property getter executes, for example in the constructor. To ignore the delegate, add the [SafeForDependencyAnalysis] attribute to the property <Property>.

Cause

The NotifyPropertyChangedAttribute aspect analyzes the getter of each property at build time to find the fields and properties that its value depends on. It cannot follow a delegate, so PostSharp reports DOM003 when the getter creates one, from a method group such as this.Save or from a lambda expression, for example one passed to a LINQ method.

The message is about the creation of the delegate. Calling a delegate in the getter reports DOM011, because the Invoke method of a delegate is virtual, so a getter that creates a delegate and calls it reports both messages. A call to a static method that receives the delegate, such as a LINQ method, can also report DOM002, because a delegate argument is not of an immutable type.

<Method> is the method that creates the delegate: the getter itself, or a method of the same class that the getter calls. PostSharp reports the message on the property.

In the following example, the getter of the SaveCommand property of DocumentViewModel creates a delegate to the Save method each time it is called.

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

namespace Errors.DOM003_Error;

public sealed class Command
{
    private readonly Action execute;

    public Command( Action execute )
    {
        this.execute = execute;
    }

    public void Execute() => this.execute();
}

[NotifyPropertyChanged]
public class DocumentViewModel
{
    public bool IsSaved { get; private set; }

    // The getter creates a delegate to the Save method.
    public Command SaveCommand => new Command( this.Save );

    private void Save()
    {
        this.IsSaved = true;
    }
}

public static class Program
{
    public static void Main()
    {
        DocumentViewModel document = new();
        ((INotifyPropertyChanged) document).PropertyChanged += ( _, e ) => Console.WriteLine( $"PropertyChanged: {e.PropertyName}" );

        document.SaveCommand.Execute();
        Console.WriteLine( $"IsSaved: {document.IsSaved}" );
    }
}
error DOM003: DOM003_Error.cs(25,20): NotifyPropertyChangedAttribute: automatic analysis of property Errors.DOM003_Error.DocumentViewModel.SaveCommand failed. Method Errors.DOM003_Error.DocumentViewModel.get_SaveCommand creates a delegate, which cannot be analyzed. To solve this error, create the delegate outside of the code that the property getter executes, for example in the constructor. To ignore the delegate, add the [SafeForDependencyAnalysis] attribute to the property Errors.DOM003_Error.DocumentViewModel.SaveCommand.

Resolution

  • Create the delegate once, outside of the getter, for example in the constructor, and store it, or the object that holds it, in a field or a read-only property.
  • When the delegate does not change the dependencies of the property, add the SafeForDependencyAnalysisAttribute attribute to the property, and declare the dependencies hidden behind the delegate with On.

In the following example, the constructor creates the command, and the SaveCommand property only returns it.

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

namespace Errors.DOM003_Fixed;

public sealed class Command
{
    private readonly Action execute;

    public Command( Action execute )
    {
        this.execute = execute;
    }

    public void Execute() => this.execute();
}

[NotifyPropertyChanged]
public class DocumentViewModel
{
    public DocumentViewModel()
    {
        this.SaveCommand = new Command( this.Save );
    }

    public bool IsSaved { get; private set; }

    // The delegate is created once, in the constructor, and the getter only reads the property.
    public Command SaveCommand { get; }

    private void Save()
    {
        this.IsSaved = true;
    }
}

public static class Program
{
    public static void Main()
    {
        DocumentViewModel document = new();
        ((INotifyPropertyChanged) document).PropertyChanged += ( _, e ) => Console.WriteLine( $"PropertyChanged: {e.PropertyName}" );

        document.SaveCommand.Execute();
        Console.WriteLine( $"IsSaved: {document.IsSaved}" );
    }
}
PropertyChanged: IsSaved
IsSaved: True

See Also

Reference

NotifyPropertyChangedAttribute
SafeForDependencyAnalysisAttribute
On

Other Resources

Handling Corner Cases of the NotifyPropertyChanged Aspect