Для обеспечения правильной реализации INotifyPropertyChanged с CodeContracts - & # 8220; требуется недоказанное & # 8221;

StackOverflow https://stackoverflow.com/questions/1621921

Вопрос

Я ищу простой способ обеспечить правильную реализацию 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;
}

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

Это было полезно?

Решение

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

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

Другие советы

Хорошо, прежде всего, для этой цели я лично использую реализацию ObservableObject из фонда MVVM . Это проверка времени выполнения только в 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-опыте), он, безусловно, выглядит как & Quot; патч & Quot; за отсутствующую статическую проверку.

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

<Ол>
  • Как говорит Марк, использовать лямбда-нотацию и извлекать строка во время выполнения .
  • Написать собственное правило FxCop .
  • Используйте инструмент AOP для пост-обработки кода с некоторой мета-разметкой.
  • Что касается CodeContracts, я считаю, что он еще недостаточно совершенен для обработки такого рода проверок в статическом анализе. Представьте, что он должен проанализировать вашу лямбду, понять, как она может быть ошибочной из-за неверного propertyName , найти все вызовы этого метода, выяснить все возможные входные данные и т. Д. Это просто неправильный инструмент для этого вид проверки.

    То, как я делал это в прошлом, - это использование нашего хорошего друга Lambda. Используя выражения, мы можем передать сами свойства вашей реализации 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;
         }
    }
    
    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top