2009-10-25 53 views
5

我正在寻找一个简单的方法来执行正确执行INotifyPropertyChanged的即时的PropertyChanged提高它必须引用实际上是定义的属性。我试着用Microsoft的新CodeContract工具来做这件事,但我不断收到警告“CodeContracts:需要未经证实的”。这是我的代码...执法的正确实施INotifyPropertyChanged的与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; 
} 

有没有办法让这个工作?

+0

静态检查器不会反射来验证合同。 http://social.msdn.microsoft.com/Forums/en-US/codecontracts/thread/37e28a50-bf64-4b02-b384-f55117735690/ – hwiechers 2009-12-03 04:12:34

回答

1

我假设你是指静态分析工具? (我希望运行时检查至少可以工作 - 而且你可能会把它留在调试版本中)。我怀疑这是静态分析将是能看到的东西,通过 - GetType().GetProperties()简直是太复杂了,等

在短;我怀疑它... lambdas(Expression)是一个选项,但它们比传递一个字符串慢得多。

+0

从技术上讲,表达式较慢 - 但担心它是一种微型优化这通常不值得。 (在我的基准测试中,我们正在谈论1800纳秒的差异,即不到2 * micro *秒。) – Bevan 2012-06-08 06:21:31

+0

@Bevan在c#5中,无论如何,这完全消失了 - 有一些新的花式机制可以获得名为[辉煌]的调用名称,例如'INotifyPropertyChanged' – 2012-06-08 06:22:40

+0

同意,[CallerMemberName](http://msdn.microsoft .com/en-us/library/system.runtime.compilerservices.callermembernameattribute%28v = vs.110%29.aspx)属性是最有用的。 – Bevan 2012-06-08 06:38:43

3

好吧,首先,为了这个目的,我个人使用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. 像马克说,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失败,找到所有对这个方法的调用,找出所有可能的输入等等。这对于这种检查来说只是一个错误的工具。

+0

+1:为此目的使用ConditionalAttribute实际上非常酷。 – Juliet 2009-11-14 00:57:53

1

我过去的做法是使用我们的好朋友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; 
    } 
}