Hacer cumplir la implementación correcta de INotifyPropertyChanged con CodeContracts - "requiere no comprobado"

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

Pregunta

Estoy buscando una manera fácil de aplicar la implementación correcta de INotifyPropertyChanged, es decir, cuando se activa PropertyChanged, debe hacer referencia a una propiedad que está realmente definida. Intenté hacer esto con las nuevas herramientas CodeContract de Microsoft, pero sigo recibiendo la advertencia '' CodeContracts: requiere sin probar ''. Aquí está mi código ...

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

¿Hay alguna forma de hacer que esto funcione?

¿Fue útil?

Solución

¿Asumo que te refieres con las herramientas de análisis estático? (Esperaría que la verificación de tiempo de ejecución funcione, al menos, y presumiblemente podría dejarla en versiones de depuración). Dudo que esto sea algo que el análisis estático pueda ver a través de - GetType (). GetProperties () es simplemente demasiado complejo, etc.

En resumen; Lo dudo ... lambdas ( Expresión ) son una opción, pero son mucho más lentas que pasar solo una cadena.

Otros consejos

Ok, antes que nada, para este propósito yo personalmente uso la implementación de ObservableObject de la MVVM Foundation . Es una comprobación de tiempo de ejecución solo DEBUG-build casi idéntica a la suya.

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

Probablemente sea la forma más fácil, pero tiene ciertas desventajas: necesita poder heredar de alguna clase base, solo funciona en tiempo de ejecución (aunque esto siempre fue suficiente en mi experiencia wpf), seguramente parece un " parche " para una comprobación estática faltante.

Tiene varias formas de habilitar el análisis estático / herramientas estáticas para este caso:

  1. Como dice Marc, usa la notación lambda y el extracto cadena en tiempo de ejecución .
  2. Escriba una regla personalizada de FxCop .
  3. Use una herramienta AOP para procesar el código con alguna metamarcación.

En cuanto a CodeContracts, creo que aún no está lo suficientemente maduro como para manejar este tipo de comprobaciones en análisis estático. Imagínese, tiene que analizar su lambda, comprender cómo puede fallar un propertyName incorrecto, encontrar todas las llamadas a este método, averiguar todas las entradas posibles, etc. Es simplemente un instrumento incorrecto para eso tipo de verificación.

La forma en que lo hice en el pasado es usar a nuestra buena amiga Lambda. Al usar Expressions podemos pasar las propiedades a su implementación de OnPropertyChanges y usar el árbol Expression para extraer la propiedad. Esto le permite verificar el tiempo de compilación de los miembros para los que está generando el evento PropertyChanged.

Por supuesto, el uso de Expression dependerá completamente del tipo de rendimiento que necesite.

Vea el fragmento de código a continuación:

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;
     }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top