Подавление многоадресной рассылки PostSharp с помощью атрибута

Недавно я начал экспериментировать с PostSharp и обнаружил особенно полезный аспект для автоматизации реализации INotifyPropertyChanged. Вы можете увидеть пример здесь. Базовая функциональность превосходна (все свойства будут уведомлены), но есть случаи, когда я мог бы захотеть подавить уведомление.

Например, я могу знать, что конкретное свойство устанавливается один раз в конструкторе и больше никогда не изменится. Таким образом, нет необходимости создавать код для NotifyPropertyChanged. Накладные расходы минимальны, когда экземпляры классов создаются нечасто, и я могу предотвратить проблему, переключившись с автоматически сгенерированного свойства на свойство, поддерживаемое полем, и записав в поле. Однако, поскольку я изучаю этот новый инструмент, было бы полезно узнать, есть ли способ пометить свойство атрибутом, чтобы подавить генерацию кода. Я хотел бы иметь возможность сделать что-то вроде этого:

[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;
    }
}

person Dan Bryant    schedule 07.03.2010    source источник


Ответы (3)


Вы можете использовать MethodPointcut вместо MulticastPointcut, т. е. использовать Linq-over-Reflection и фильтровать по PropertyInfo.IsDefined (вашему атрибуту).

  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);
        }
    }
person Gael Fraiteur    schedule 07.03.2010
comment
Отлично, спасибо. Это очень мощный инструмент, и мой проект выглядит значительно чище без всей утомительной рутины INotifyPropertyChanged. - person Dan Bryant; 07.03.2010

Другой простой метод, используйте:

[NotifyPropertyChanged(AttributeExclude=true)]

... чтобы подавить атрибуты для определенного метода. Это работает, даже если к классу прикреплен глобальный атрибут (как в приведенном выше примере).

Вот полный код примера:

[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;
    }
}

Как только вы добавите эту строку, PostSharp даже обновит графический интерфейс MSVS, удалив подчеркивание, указывающее, какие атрибуты присоединены к методу. Конечно, если вы запустите отладчик, он пропустит выполнение каких-либо атрибутов для этого конкретного метода.

person Contango    schedule 02.11.2010

К вашему сведению, у меня возникли некоторые проблемы с примером на веб-сайте Sharpcrafters, и мне пришлось внести следующие изменения:

    /// <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;

Я думаю, что он вводил член OnPropertyChanged, но импортировал его до того, как он был создан. Это приведет к сбою OnPropertySet, поскольку this.OnPropertyChangedMethod имеет значение null.

person Dan Bryant    schedule 09.03.2010