Microsoft should have implemented something snappy for INotifyPropertyChanged
, like in the automatic properties, just specify {get; set; notify;}
I
Let me introduce my own approach called Yappi. It belongs to Runtime proxy|derived class generators, adding new functionality to an existing object or type, like Caste Project's Dynamic Proxy.
It allows to implement INotifyPropertyChanged once in base class, and then declare derived classes in following style, still supporting INotifyPropertyChanged for new properties:
public class Animal:Concept
{
protected Animal(){}
public virtual string Name { get; set; }
public virtual int Age { get; set; }
}
Complexity of derived class or proxy construction can be hidden behind the following line:
var animal = Concept.Create.New();
And all INotifyPropertyChanged implementation work can be done like this:
public class Concept:INotifyPropertyChanged
{
//Hide constructor
protected Concept(){}
public static class Create where TConcept:Concept
{
//Construct derived Type calling PropertyProxy.ConstructType
public static readonly Type Type = PropertyProxy.ConstructType>(new Type[0], true);
//Create constructing delegate calling Constructor.Compile
public static Func New = Constructor.Compile>(Type);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
{
var caller = PropertyChanged;
if(caller!=null)
{
caller(this, eventArgs);
}
}
//define implementation
public class Implementation : DefaultImplementation where TConcept:Concept
{
public override Func OverrideGetter(PropertyInfo property)
{
return PropertyImplementation.GetGetter(property.Name);
}
///
/// Overriding property setter implementation.
///
/// Base type for implementation. TBaseType must be TConcept, and inherits all its constraints. Also TBaseType is TDeclaringType.
/// Type, declaring property.
/// Constructed type. TConstructedType is TDeclaringType and TBaseType.
/// Type of property.
/// PropertyInfo of property.
/// Delegate, corresponding to property setter implementation.
public override Action OverrideSetter(PropertyInfo property)
{
//This code called once for each declared property on derived type's initialization.
//EventArgs instance is shared between all events for each concrete property.
var eventArgs = new PropertyChangedEventArgs(property.Name);
//get delegates for base calls.
Action setter = PropertyImplementation.GetSetter(property.Name);
Func getter = PropertyImplementation.GetGetter(property.Name);
var comparer = EqualityComparer.Default;
return (pthis, value) =>
{//This code executes each time property setter is called.
if (comparer.Equals(value, getter(pthis))) return;
//base. call
setter(pthis, value);
//Directly accessing Concept's protected method.
pthis.OnPropertyChanged(eventArgs);
};
}
}
}
It is fully safe for refactoring, uses no reflection after type construction and fast enough.