715 votes

Implémentation de INotifyPropertyChanged - existe-t-il un meilleur moyen?

Microsoft doit avoir mis en œuvre quelque chose d'accrocheur pour INotifyPropertyChanged, comme dans le système automatique de propriétés, il suffit de spécifier {get; set; notify;} Je pense que cela fait beaucoup de sens pour le faire. Ou il y a des complications pour le faire?

Pouvons-nous mettre en place quelque chose comme "informer" dans nos propriétés. Est-il une solution élégante pour la mise en œuvre de INotifyPropertyChanged dans votre classe ou la seule façon de le faire est par le relèvement de l' PropertyChanged événement dans chaque propriété.

Si non, peut on écrire quelque chose de générer automatiquement le morceau de code de lever PropertyChanged événement?

698voto

Marc Gravell Points 482669

Sans l'aide de quelque chose comme postsharp, la version minimale-je utiliser utilise quelque chose comme:

public class Data : INotifyPropertyChanged
{
    // boiler-plate
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    protected bool SetField<T>(ref T field, T value, string propertyName)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    // props
    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }
}

Chaque propriété est simplement quelque chose comme:

    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, "Name"); }
    }

ce qui n'est pas énorme; elle peut également être utilisée comme classe de base si vous le souhaitez. L' bool retour d' SetField vous dit si c'était un no-op, dans le cas où vous souhaitez appliquer une autre logique.


ou encore plus facile avec C# 5:

protected bool SetField<T>(ref T field, T value,
    [CallerMemberName] string propertyName = null)
{...}

et:

set { SetField(ref name, value); }

avec le compilateur va ajouter l' "Name" automatiquement.

204voto

Daniel Little Points 4451

Comme de .Net 4.5, il est enfin un moyen facile de le faire.

.Net 4.5 introduit une nouvelle les Informations sur l'Appelant Attributs.

private void OnPropertyChanged<T>([CallerMemberName]string caller = null) {
     // make sure only to call this if the value actually changes

     var handler = PropertyChanged;
     if (handler != null) {
        handler(this, new PropertyChangedEventArgs(caller));
     }
}

C'est probablement une bonne idée d'ajouter un comparateur à la fonction.

EqualityComparer<T>.Default.Equals

D'autres exemples ici et ici

Voir aussi les Informations sur l'Appelant (C# et Visual Basic)

166voto

Thomas Levesque Points 141081

J'aime vraiment de Marc solution, mais je pense qu'il peut être légèrement améliorée afin d'éviter à l'aide d'une "magie de la chaîne" (qui ne prend pas en charge refactoring). Au lieu d'utiliser le nom de la propriété comme une chaîne de caractères, il est facile d'en faire une expression lambda :

private string name;
public string Name
{
    get { return name; }
    set { SetField(ref name, value, () => Name); }
}

Il suffit d'ajouter les méthodes suivantes pour Marc du code, il fera l'affaire :

protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
    if (selectorExpression == null)
        throw new ArgumentNullException("selectorExpression");
    MemberExpression body = selectorExpression.Body as MemberExpression;
    if (body == null)
        throw new ArgumentException("The body must be a member expression");
    OnPropertyChanged(body.Member.Name);
}

protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
{
    if (EqualityComparer<T>.Default.Equals(field, value)) return false;
    field = value;
    OnPropertyChanged(selectorExpression);
    return true;
}

BTW, cela a été inspirée par ce blog, mis à jour URL

132voto

Tom Gilder Points 172

Il y a aussi Fody qui a un complément PropertyChanged , qui vous permet d'écrire ceci:

 [ImplementPropertyChanged]
public class Person 
{        
    public string GivenNames { get; set; }
    public string FamilyName { get; set; }
}
 

... et au moment de la compilation injecte des notifications de modification de propriété.

70voto

Peijen Points 48

Je pense que les gens devraient payer un peu plus d'attention à la performance, il ne fait vraiment l'impact de l'INTERFACE utilisateur lorsque il y a beaucoup d'objets à lier (pensez à une grille avec+ de 10 000 lignes) ou si l'objet de valeur change fréquemment (suivi en temps réel de l'app).

J'ai pris différents de la mise en œuvre d'ici et d'ailleurs et fait une comparaison, vérifier la performance de la comparaison de INotifyPropertyChanged implémentations.


Voici un coup d'oeil sur le résultat Implemenation vs Runtime

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X