INotifyPropertyChanged sin quemar nombre de propiedad

Aunque la información del caller demuestra ser muy útil para la depuración, existe un uso colateral muy interesante sobretodo para el mundo de las apps, donde el binding es muy usado.

En las apps es muy frecuente enlazar datos a vistas a través de paradigmas como MVVM. Y este enlace de datos requiere de clases que soporten notificación de cambios en las propiedades, de manera que la interfaz siempre permanezca sincronizada con los valores a los que está ligada y que se encuentra mostrando.

Es así como si tenemos una clase del Modelo, destinada a ligarse a una vista bien sea a través de un controlador o de una clase VistaModelo, lo más común es que queramos que implemente INotifyPropertyChanged con el fin de que se incluya el evento de tipo PropertyChangedEventHandler que se dispara cada vez que una propiedad cambia.

Esto se logra de la siguiente manera:

 class Person:INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    string _name;
    public string Name
    {
        get { return _name; }
        set 
        {
            _name = value;
            NotifyPropertyChanged("Name");
        }
    }

    int _age;
    public int Age
    {
        get { return _age; }
        set 
        { 
            _age = value;
            NotifyPropertyChanged("Age");
        }
    }

    private void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, 
              new PropertyChangedEventArgs(propertyName));
        }
    }
}

Como observamos, siempre nos toca poner como parámetro de NotifyPropertyChanged el nombre de la propiedad en un string. Esto puede generar algunos problemas sobretodo a la hora del refactoring tras cambiar el nombre de alguna propiedad, pues dado que estos parámetros son strings, el refactoring no cambiará automáticamente estos valores y además no podríamos detectar el error en tiempo de compilación, sino que obtendríamos una excepción difícil de descifrar en tiempo de ejecución si cambiamos por ejemplo Name por SecondName.

Para evitar hacer estas referencias quemadas como strings, podemos aprovechar el hecho de que el Framework 4.5 incluye información del caller. Así pues, lo único que hacemos es citar el nombre del miembro que hizo el llamado que provocó el cambio en la propiedad. En este caso, ese nombre, es precisamente el nombre de la propiedad que se está modificando, así que independientemente de que cambie, siempre se referenciará la propiedad adecuada:

 class Person:INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        string _name;
        public string Name
        {
            get { return _name; }
            set 
            {
                _name = value;
                NotifyPropertyChanged();
            }
        }

        int _age;
        public int Age
        {
            get { return _age; }
            set 
            { 
                _age = value;
                NotifyPropertyChanged();
            }
        }

        private void NotifyPropertyChanged(
          [CallerMemberName] string propertyName="")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, 
                  new PropertyChangedEventArgs(propertyName));
            }
        }
    }

Como se observa, ya no es necesario quemar el nombre de la propiedad, dado que CallerMemberName representará ese nombre en el parámetro propertyName que al ser opcional, no requiere ser llamado por nosotros, sino que es llenado por el servicio de compilación. Esta es una práctica que espero tendrá una amplia difusión en las apps de Windows 8 y de Windows Phone 8.