en mi humilde opinión, el enfoque PostSharp, como en la respuesta aceptada, es muy agradable y es por supuesto la respuesta directa a la pregunta formulada.
Sin embargo, para aquellos que no pueden o no usarán una herramienta como PostSharp para extender la sintaxis del lenguaje C#, se puede obtener la mayor ventaja de evitar la repetición del código con una clase base que implementa INotifyPropertyChanged
. Hay muchos ejemplos por ahí, pero ninguno hasta ahora han sido incluidos en esta pregunta útil y bien víctimas de ella, así que aquí es la versión generalmente utilizo:
/// <summary>
/// Base class for classes that need to implement <see cref="INotifyPropertyChanged"/>
/// </summary>
public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
/// <summary>
/// Raised when a property value changes
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Updates a field for a named property
/// </summary>
/// <typeparam name="T">The type of the field</typeparam>
/// <param name="field">The field itself, passed by-reference</param>
/// <param name="newValue">The new value for the field</param>
/// <param name="propertyName">The name of the associated property</param>
protected void UpdatePropertyField<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(field, newValue))
{
field = newValue;
OnPropertyChanged(propertyName);
}
}
/// <summary>
/// Raises the <see cref="PropertyChanged"/> event.
/// </summary>
/// <param name="propertyName">The name of the property that has been changed</param>
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.DynamicInvoke(this, new PropertyChangedEventArgs(propertyName));
}
}
utilizar, por ejemplo, así:
private int _value;
public int Value
{
get { return _value; }
set { UpdatePropertyField(ref _value, value); }
}
No es tan conciso como poder aplicar un atributo de código a una propiedad implementada automáticamente como en el enfoque PostSharp, pero aún ayuda a acelerar la implementación de modelos de vista y otros tipos similares.
Las características principales anteriores que lo distinguen de otras implementaciones:
- igualdad se compararon mediante
EqualityComparer<T>.Default
. Esto asegura que los tipos de valores se pueden comparar sin encasillar (una alternativa común sería object.Equals(object, object)
). La instancia IEqualityComparer<T>
está en caché, por lo que después de la primera comparación para cualquier tipo dado T
, es muy eficiente.
- El método
OnPropertyChanged()
es virtual
. Esto permite que los tipos derivados manejen fácil y eficientemente los eventos cambiados de propiedad de forma centralizada, sin tener que suscribirse al evento PropertyChanged
mismo (por ejemplo, para múltiples niveles de herencia) y también le da al tipo derivado un mejor control sobre cómo y cuándo maneja la propiedad cambió el evento relativo al aumento del evento real PropertyChanged
.
¿No debería siempre verificar siempre 'if (m_Fieldname! = Value) {...}'? Es más código, lo sé, pero subir 'PropertyChanged' no parece correcto si la propiedad no cambia. –
@Danko, gracias buen punto –
Personalmente, tengo un método SetProperty en una clase base de ObservableItem (de la cual deriva mi ViewModelBase) que maneja todas las notificaciones, comprobación de igualdad, configuración de propiedades, etc. Es agradable y limpio, y usted todavía solo tiene juegos y conspiraciones de una sola línea. Además, solo configure un fragmento de código para crearlos, y es rápido, simple y estandarizado. –