代码之家  ›  专栏  ›  技术社区  ›  hwiechers

使用codecontracts强制正确实现inotifyPropertyChanged-“需要未经验证”

  •  5
  • hwiechers  · 技术社区  · 15 年前

    我正在寻找一种简单的方法来强制执行INotifyPropertyChanged的正确实现,即当引发PropertyChanged时,它必须引用实际定义的属性。我试着用微软的新codecontract工具来做这个,但是我一直收到“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 回复  |  直到 14 年前
        1
  •  1
  •   Marc Gravell    15 年前

    我想你是说静态分析工具?(我希望运行时检查可以工作,至少-您可以将其留在调试版本中)。我怀疑这是静态分析能够看穿的东西- GetType().GetProperties() 太复杂了等等。

    简而言之,我怀疑…兰姆达斯 Expression )是一个选项,但它们比传递字符串慢得多。

        2
  •  3
  •   Massimiliano    15 年前

    首先,为了这个目的,我个人使用 MVVM foundation . 它是一个只调试构建的运行时检查,几乎与您的相同。

    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. 就像贾景晖说的, use lambda notation and extract string in run-time .
    2. Write a custom FxCop rule .
    3. Use an AOP tool to post-process code 有一些元标记。

    至于codecontracts,我认为在静态分析中处理这种检查还不够成熟。想象一下,它必须解析你的lambda,理解它是如何因错误而失败的。 propertyName 找到对这个方法的所有调用,找出所有可能的输入,等等。对于这种检查,这只是一个错误的工具。

        3
  •  1
  •   Mark Allanson    14 年前

    我过去这样做的方式是使用我们的好朋友lambda。通过使用表达式,我们可以将属性本身传递给onPropertyChanges的实现,并使用表达式树提取属性。这使您可以在编译时检查要为其引发PropertyChanged事件的成员。

    当然,表达式的使用将完全取决于您需要的性能类型。

    请参见下面的代码段:

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