L'application correcte de la propriété INotifyPropertyChanged à l'aide de CodeContracts - & # 8220; requiert des ressources non éprouvées & # 8221;

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

Question

Je cherche un moyen simple de faire en sorte que l'implémentation correcte d'INotifyPropertyChanged soit appliquée. Par exemple, lorsque PropertyChanged est généré, il doit faire référence à une propriété réellement définie. J’ai essayé de le faire avec les nouveaux outils CodeContract de Microsoft, mais l’avertissement "CodeContracts: nécessite non éprouvé" continue de me parvenir. Voici mon code ...

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

Y a-t-il un moyen de faire en sorte que cela fonctionne?

Était-ce utile?

La solution

Je suppose que vous voulez dire avec les outils d'analyse statique? (Je pense que la vérification de l'exécution fonctionnera au moins - et vous pourriez probablement la laisser dans les versions de débogage). Je doute que l'analyse statique puisse voir cela d'une manière approfondie - GetType (). GetProperties () est tout simplement trop complexe, etc.

En bref; J'en doute ... Les lambdas ( Expression ) sont une option, mais ils sont beaucoup plus lents que de simplement passer une chaîne.

Autres conseils

Ok, tout d’abord, j’utilise personnellement l’implémentation ObservableObject de la fondation MVVM . Il s’agit d’un contrôle d’exécution de DEBUG uniquement construit, presque identique au vôtre.

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

C’est probablement le moyen le plus simple, mais il présente certains inconvénients: vous devez pouvoir hériter d’une classe de base, cela ne fonctionne que dans le temps d’exécution (bien que cela ait toujours suffi dans mon expérience avec wpf), cela ressemble sûrement à un "patch" pour une vérification statique manquante.

Vous avez plusieurs possibilités pour activer l'analyse statique / les outils statiques dans ce cas:

  1. Comme le dit Marc, utilise la notation lambda et extrait chaîne au moment de l'exécution .
  2. Écrire une règle FxCop personnalisée .
  3. Utiliser un outil AOP pour post-traiter le code avec une méta-balise.

En ce qui concerne CodeContracts, je pense qu’il n’est pas encore suffisamment développé pour gérer ce type de contrôle en analyse statique. Imaginez, il doit analyser votre lambda, comprendre comment il peut échouer avec un nom de propriété erroné, rechercher tous les appels à cette méthode, déterminer toutes les entrées possibles, etc. genre de chèque.

Par le passé, j'ai utilisé notre bon ami Lambda. En utilisant Expressions, nous pouvons transmettre les propriétés elles-mêmes à votre implémentation de OnPropertyChanges et utiliser l'arborescence Expression pour extraire la propriété. Cela vous donne la vérification du temps de compilation des membres pour lesquels vous élevez l'événement PropertyChanged.

Bien sûr, l'utilisation d'Expression dépend entièrement du type de performance dont vous avez besoin.

Voir l'extrait de code ci-dessous:

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;
     }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top