2009-10-25 19 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; 
    } 
}