2009-10-25 6 views
5

Szukam łatwego sposobu wymuszenia poprawnej implementacji INotifyPropertyChanged, tj. Gdy podniesiony jest PropertyChanged, musi odwoływać się do właściwości, która jest faktycznie zdefiniowana. Próbowałem to zrobić za pomocą nowych narzędzi CodeContract firmy Microsoft, ale nadal otrzymuję ostrzeżenie "CodeContracts: wymaga niesprawdzonych". Oto mój kod ...Egzekwowanie poprawnej implementacji INotifyPropertyChanged z CodeContracts - "wymaga nieudowodnionej"

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

Czy jest jeszcze jakiś sposób, aby to zadziałało?

+0

Kontrolujący statyczne nie odbicie celu potwierdzenia zamówienia. http://social.msdn.microsoft.com/Forums/en-US/codecontracts/thread/37e28a50-bf64-4b02-b384-f55117735690/ – hwiechers

Odpowiedz

1

Zakładam, że masz na myśli narzędzia do analizy statycznej? (Oczekuję przynajmniej, że sprawdzenie działania będzie działało - i prawdopodobnie można zostawić to w kompilacjach debugowania). Wątpię, czy to jest coś, co statyczna analiza będzie w stanie przejrzeć - GetType().GetProperties() jest po prostu zbyt skomplikowana, itp.

W skrócie; Wątpię, że ... lambdas (Expression) są opcją, ale są znacznie wolniejsze niż podanie tylko ciągu znaków.

+0

Wyrażenia są wolniejsze - ale martwienie się o nie jest mikro-optymalizacją to zazwyczaj nie jest warte wysiłku. (W moich testach porównawczych mówimy o różnicy 1800 nanosekund, czyli mniej niż 2 * mikro * sekundy.) – Bevan

+0

@Bevan w C# 5, to całkowicie odejdzie w każdym razie - są nowe fantazyjne mechanizmy uzyskiwania nazwy wywołującej, które są * genialne * dla rzeczy takich jak 'INotifyPropertyChanged' –

+0

Uzgodnione, [CallerMemberName] (http://msdn.microsoft Atrybuty .com/en-us/library/system.runtime.compilerservices.callermembernameattribute% 28v = vs.110% 29.aspx) są najbardziej przydatne. – Bevan

3

Ok, przede wszystkim do tego celu osobiście używam implementacji ObservableObject z MVVM foundation. Jest to sprawdzanie tylko w czasie wykonywania DEBUG, prawie identyczne z twoim.

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

To chyba najprostszy sposób, ale ma pewne wady: trzeba, aby móc dziedziczyć z jakiejś klasie bazowej, to działa tylko w czasie pracy (chociaż to było wystarczająco zawsze w moim WPF doświadczenia), to z pewnością wygląda jak "łatka" dla brakującego czeku statycznego.

Masz kilka sposobów umożliwiających analizy statycznej/narzędzia statyczne w tym przypadku:

  1. Podobnie jak Marc mówi use lambda notation and extract string in run-time.
  2. Write a custom FxCop rule.
  3. Use an AOP tool to post-process code z niektórymi meta-znacznikami.

Jeśli chodzi o CodeContracts, uważam, że nie jest jeszcze wystarczająco dojrzały, aby obsłużyć tego rodzaju kontrole w analizie statycznej. Wyobraź sobie, że musisz przeanalizować swoją lambdę, zrozumieć, w jaki sposób może to być spowodowane błędem propertyName, znaleźć wszystkie wywołania tej metody, dowiedzieć się wszystkich możliwych danych wejściowych itd. Jest to po prostu zły instrument dla tego rodzaju kontroli.

+0

+1: użycie ConditionalAttribute w tym celu jest całkiem fajne. – Juliet

1

Sposób, w jaki to zrobiłem w przeszłości, to wykorzystanie naszej dobrej przyjaciółki Lambdy. Korzystając z Wyrażeń, możemy przekazać właściwości do implementacji OnPropertyChanges i użyć drzewa Wyrażenie, aby wyodrębnić właściwość. To daje ci możliwość sprawdzania czasu kompilacji członków, dla których wychowujesz zdarzenie PropertyChanged.

Oczywiście użycie Expression zależy wyłącznie od tego, jakiego rodzaju wydajności potrzebujesz.

kod widoczny fragment poniżej:

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; 
    } 
} 
Powiązane problemy