Impor a aplicação correcta da INotifyPropertyChanged com CodeContracts - “requer não comprovada”

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

Pergunta

Eu estou procurando uma maneira fácil de fazer valer a aplicação correcta da INotifyPropertyChanged ou seja, quando PropertyChanged é levantada deve fazer referência a uma propriedade que é realmente definido. Eu tentei fazer isso com as novas ferramentas CodeContract da Microsoft, mas eu continuo recebendo o aviso "CodeContracts: requer não comprovada". Aqui está o meu 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;
}

Existe uma maneira de chegar a este trabalho?

Foi útil?

Solução

Eu suponho que você quer dizer com as ferramentas de análise estática? (Eu esperaria a verificação de tempo de execução para o trabalho, pelo menos - e você poderia provavelmente deixá-lo em compilações de depuração). Duvido que isso é algo que a análise estática vai ser capaz de ver através -. GetType().GetProperties() é simplesmente demasiado complexo, etc

Em suma; Duvido ... lambdas (Expression) são uma opção, mas eles são muito mais lentos do que passar apenas uma string.

Outras dicas

Ok, antes de tudo, para este efeito, eu pessoalmente uso implementação ObservableObject do MVVM fundação . É uma depuração-build única runtime verificar quase idêntico ao seu.

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

É provavelmente a maneira mais fácil, mas tem algumas desvantagens: você precisa ser capaz de herdar de uma classe base, ele só funciona em tempo de execução (embora este sempre foi o suficiente no meu WPF-experiência), ele certamente parece uma "patch" para uma verificação estática faltando.

Você tem várias maneiras de permitir a análise estática / ferramentas estáticos para este caso:

  1. Como Marc diz: notação uso lambda e extrato string no tempo de execução .
  2. escrever um personalizado regra FxCop .
  3. Use uma ferramenta de AOP ao código de pós-processo com alguma meta-marcação.

Quanto aos CodeContracts, eu acredito que ainda não é maduro o suficiente para lidar com este tipo de cheques em análise estática. Imaginar, ele tem que analisar o seu lambda, entender como ele pode ser falhou por um propertyName errado, encontrar todas as chamadas para este método, descobrir todas as entradas possíveis, etc. É apenas um instrumento errado para esse tipo de verificação.

A maneira que eu tenho feito isso no passado é usar o nosso bom amigo Lambda. Usando expressões podemos passar nas próprias propriedades para sua implementação do OnPropertyChanges, e usar a árvore de expressão para extrair a propriedade. Isto dá-lhe a verificação de tempo de compilação dos membros você está aumentando o evento PropertyChanged para.

De uso curso de Expressão dependerá inteiramente de que tipo de desempenho que você precisa.

Veja trecho de código abaixo:

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top