Suppressing PostSharp Multicast with Attribute
Asked Answered
G

3

6

I've recently started experimenting with PostSharp and I found a particularly helpful aspect to automate implementation of INotifyPropertyChanged. You can see the example here. The basic functionality is excellent (all properties will be notified), but there are cases where I might want to suppress notification.

For instance, I might know that a particular property is set once in the constructor and will never change again. As such, there is no need to emit the code for NotifyPropertyChanged. The overhead is minimal when classes are not frequently instantiated and I can prevent the problem by switching from an automatically generated property to a field-backed property and writing to the field. However, as I'm learning this new tool, it would be helpful to know if there is a way to tag a property with an attribute to suppress the code generation. I'd like to be able to do something like this:

[NotifyPropertyChanged]
public class MyClass
{
    public double SomeValue { get; set; }

    public double ModifiedValue { get; private set; }

    [SuppressNotify]
    public double OnlySetOnce { get; private set; }

    public MyClass()
    {
        OnlySetOnce = 1.0;
    }
}
Goutweed answered 7/3, 2010 at 18:3 Comment(0)
A
5

You can use a MethodPointcut instead of a MulticastPointcut, i.e. use Linq-over-Reflection and filter against PropertyInfo.IsDefined(your attribute).

  private IEnumerable<PropertyInfo> SelectProperties( Type type )
    {
        const BindingFlags bindingFlags = BindingFlags.Instance | 
            BindingFlags.DeclaredOnly
                                          | BindingFlags.Public;

        return from property
                   in type.GetProperties( bindingFlags )
               where property.CanWrite &&
                     !property.IsDefined(typeof(SuppressNotify))
               select property;
    }

    [OnLocationSetValueAdvice, MethodPointcut( "SelectProperties" )]
    public void OnSetValue( LocationInterceptionArgs args )
    {
        if ( args.Value != args.GetCurrentValue() )
        {
            args.ProceedSetValue();

           this.OnPropertyChangedMethod.Invoke(null);
        }
    }
Aaronson answered 7/3, 2010 at 18:46 Comment(1)
Excellent, thank you. This is a very powerful tool and my project feels significantly cleaner without all the tedious INotifyPropertyChanged plumbing.Goutweed
K
3

Another easy method, use:

[NotifyPropertyChanged(AttributeExclude=true)]

... to suppress the attributes for a particular method. This works even if there is a global attribute attached to the class (as in the example above).

Here is the full example code:

[NotifyPropertyChanged]
public class MyClass
{
    public double SomeValue { get; set; }

    public double ModifiedValue { get; private set; }

    [NotifyPropertyChanged(AttributeExclude=True)]
    public double OnlySetOnce { get; private set; }

    public MyClass()
    {
        OnlySetOnce = 1.0;
    }
}

Once you add this line, PostSharp will even update the MSVS GUI to remove the underline indicating which attributes are attached to the method. Of course, if you run the debugger, it will skip executing any attributes on that particular method.

Kinson answered 2/11, 2010 at 18:29 Comment(0)
G
0

FYI, I had some problems with the example on the Sharpcrafters website and had to make the following change:

    /// <summary>
    /// Field bound at runtime to a delegate of the method <c>OnPropertyChanged</c>.
    /// </summary>
    [ImportMember("OnPropertyChanged", IsRequired = true, Order = ImportMemberOrder.AfterIntroductions)]
    public Action<string> OnPropertyChangedMethod;

I think it was introducing the OnPropertyChanged member, but importing before it had a chance to be created. This would cause the OnPropertySet to fail because this.OnPropertyChangedMethod was null.

Goutweed answered 9/3, 2010 at 3:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.