Обеспечение правильной реализации INotifyPropertyChanged с CodeContracts - «требуется недоказанное» - PullRequest
5 голосов
/ 25 октября 2009

Я ищу простой способ обеспечить правильную реализацию INotifyPropertyChanged, то есть когда вызывается PropertyChanged, он должен ссылаться на свойство, которое фактически определено. Я пытался сделать это с помощью новых инструментов CodeContract от Microsoft, но получаю предупреждение «CodeContracts: требуется недоказано». Вот мой код ...

public sealed class MyClass : INotifyPropertyChanged
{
    private int myProperty;
    public int MyProperty
    {
        get
        {
            return myProperty;
        }
        set
        {
            if (myProperty == value)
            {
                return;
            }

            myProperty = value;
            OnPropertyChanged("MyProperty");
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        Contract.Requires(GetType().GetProperties().Any(x => x.Name == propertyName));

        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Есть ли способ заставить это работать?

Ответы [ 3 ]

3 голосов
/ 30 октября 2009

Хорошо, прежде всего, для этой цели я лично использую реализацию ObservableObject из MVVM Foundation . Это проверка во время выполнения только для DEBUG-сборки, почти идентичная вашей.

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged(string propertyName)
{
    this.VerifyPropertyName(propertyName);

    PropertyChangedEventHandler handler = this.PropertyChanged;
    if (handler != null)
    {
        var e = new PropertyChangedEventArgs(propertyName);
        handler(this, e);
    }
}

[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
    // Verify that the property name matches a real,  
    // public, instance property on this object.
    if (TypeDescriptor.GetProperties(this)[propertyName] == null)
    {
        string msg = "Invalid property name: " + propertyName;

        if (this.ThrowOnInvalidPropertyName)
            throw new Exception(msg);
        else
            Debug.Fail(msg);
    }
}

Вероятно, это самый простой способ, но у него есть определенные недостатки: вам нужно иметь возможность наследовать от некоторого базового класса, он работает только во время выполнения (хотя этого всегда было достаточно в моем wpf-опыте), он, безусловно, выглядит как «патч» для отсутствующей статической проверки.

У вас есть несколько способов включить статический анализ / статические инструменты для этого случая:

  1. Как говорит Марк, использует лямбда-нотацию и извлекает строку во время выполнения .
  2. Написать пользовательское правило FxCop .
  3. Используйте инструмент AOP для пост-обработки кода с некоторой мета-разметкой.

Что касается CodeContracts, я считаю, что он еще недостаточно совершенен для обработки такого рода проверок в статическом анализе. Представьте себе, что он должен проанализировать вашу лямбду, понять, как она может быть ошибочной из-за неправильного propertyName, найти все вызовы этого метода, выяснить все возможные входы и т. Д. Это просто неправильный инструмент для такого рода проверки. 1025 *

1 голос
/ 14 ноября 2009

Способ, которым я делал это в прошлом, - это использовать нашего хорошего друга Лямбду. Используя выражения, мы можем передать сами свойства вашей реализации OnPropertyChanges и использовать дерево выражений для извлечения свойства. Это дает вам проверку времени компиляции членов, для которых вы вызываете событие PropertyChanged.

Конечно, использование Expression будет полностью зависеть от того, какой тип производительности вам нужен.

См. Фрагмент кода ниже:

using System;
using System.Linq;
using System.ComponentModel;
using System.Linq.Expressions;

namespace OnNotifyUsingLambda
{
    public class MainClass : INotifyPropertyChanged
    {
         public static void Main (string[] args) { new MainClass().Run();}
         public void Run()
         {
              this.PropertyChanged += (sender, e) => Console.WriteLine(e.PropertyName);
              MyProperty = "Hello";
         }

         private string myProperty;
         public string MyProperty  
         {
             get
             {
                 return myProperty;
             }
             set
             {
                 myProperty = value;
                 // call our OnPropertyChanged with our lamba expression, passing ourselves.
                 // voila compile time checking that we haven't messed up!
                 OnPropertyChanged(x => x.MyProperty); 
              }
         }  

         /// <summary>
         /// Fires the PropertyChanged for a property on our class.
         /// </summary>
         /// <param name="property">
         /// A <see cref="Expression<Func<MainClass, System.Object>>"/> that contains the 
         /// property we want to raise the event for.
         /// </param>
         private void OnPropertyChanged (Expression<Func<MainClass, object>> property)
         {
             // pull out the member expression (ie mainClass.MyProperty)
             var expr = (MemberExpression)property.Body; 

             if (PropertyChanged != null)
             {
                 // Extract everything after the period, which is our property name.
                 var propName = expr.ToString ().Split (new[] { '.' })[1];
                 PropertyChanged (this, new PropertyChangedEventArgs(propName));
             }
          }

          public event PropertyChangedEventHandler PropertyChanged;
     }
}
1 голос
/ 25 октября 2009

Полагаю, вы имеете в виду инструменты статического анализа? (Я ожидаю, что проверка во время выполнения сработает, по крайней мере - и вы, вероятно, можете оставить ее в отладочных сборках). Я сомневаюсь, что это то, что статический анализ сможет увидеть - GetType().GetProperties() просто слишком сложно и т. Д.

Короче говоря; Я сомневаюсь в этом ... лямбды (Expression) - вариант, но они намного медленнее, чем передача только строки.

...