Implementación de INotifyPropertyChanged: ¿existe una forma mejor?

Resuelto P.K asked hace 15 años • 36 respuestas

Microsoft debería haber implementado algo ágil INotifyPropertyChanged, como en las propiedades automáticas, solo especifique. {get; set; notify;} Creo que tiene mucho sentido hacerlo. ¿O hay alguna complicación para hacerlo?

¿Podemos implementar nosotros mismos algo como 'notificar' en nuestras propiedades? ¿Existe una solución elegante para implementar INotifyPropertyChangeden su clase o la única forma de hacerlo es generando el PropertyChangedevento en cada propiedad?

Si no, ¿podemos escribir algo para generar automáticamente el fragmento de código para generar el PropertyChanged evento?

P.K avatar Aug 22 '09 16:08 P.K
Aceptado

Sin usar algo como postsharp, la versión mínima que uso usa algo como:

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

Entonces, cada propiedad es algo así como:

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

que no es enorme; también se puede utilizar como clase base si lo desea. El boolretorno de SetFieldle indica si fue una operación no operativa, en caso de que desee aplicar otra lógica.


o incluso más fácil con C# 5:

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

que se puede llamar así:

set { SetField(ref name, value); }

con lo cual el compilador agregará "Name"automáticamente.


C# 6.0 facilita la implementación:

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

...y ahora con C#7:

protected void OnPropertyChanged(string propertyName)
   => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

protected bool SetField<T>(ref T field, T value,[CallerMemberName] string propertyName =  null)
{
    if (EqualityComparer<T>.Default.Equals(field, value)) return false;
    field = value;
    OnPropertyChanged(propertyName);
    return true;
}

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

Y, con C# 8 y tipos de referencia anulables, se vería así:

public event PropertyChangedEventHandler? PropertyChanged;

protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = "")
{
    if (EqualityComparer<T>.Default.Equals(field, value)) return false;
    field = value;
    OnPropertyChanged(propertyName);
    return true;
}

private string name;
public string Name
{
    get => name;
    set => SetField(ref name, value);
}
Marc Gravell avatar Aug 22 '2009 16:08 Marc Gravell

.Net 4.5 introdujo los atributos de información de la persona que llama:

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

Probablemente también sea una buena idea agregar un comparador a la función.

EqualityComparer<T>.Default.Equals

Más ejemplos aquí y aquí

Consulte también Información de la persona que llama (C# y Visual Basic)

Daniel Little avatar May 15 '2012 07:05 Daniel Little