Static Reflection: Say What?
There’s a new oxymoron going around call Static Reflection. The basic gist is that by using expression trees you can do things that appear to be dynamic but in reality are checked at compile time. Like for instance, getting the name of a property for firing an INotifyPropertyChanged.PropertyChanged event. My colleague, Jon Fuller, showed me his code for Method Guards and my first comment was that this would be a great tool for implementing INotifyPropertyChanged. He informed me that he already went there and showed me that code as well.
I wasn’t too happy with the idea of wasting your sole base class on NotifyPropertyChanged so I suggested that we use extension methods instead. After a bit of finagling with the framework (Events don’t like to be fired from outside their declaring class), here is the result:
public static void SetProperty<T>(
this INotifyPropertyChanged source,
Expression<Func<T>> propExpr,
Expression<Func<T>> fieldExpr,
T value)
{
source.SetProperty(
propExpr,
fieldExpr,
value,
() => { });
}
public static void SetProperty<T>(
this INotifyPropertyChanged source,
Expression<Func<T>> propExpr,
Expression<Func<T>> fieldExpr,
T value,
Action doIfChanged)
{
var prop =
(PropertyInfo)
((MemberExpression)propExpr.Body).Member;
var field =
(FieldInfo)
((MemberExpression)fieldExpr.Body).Member;
var currVal = (T)prop.GetValue(source, null);
if (currVal==null && value==null)
return;
if (currVal==null || !currVal.Equals(value))
{
field.SetValue(source, value);
var eventDelegate =
(MulticastDelegate)
source.GetType().GetField(
"PropertyChanged",
BindingFlags.Instance |
BindingFlags.NonPublic).
GetValue(source);
Delegate[] delegates =
eventDelegate.GetInvocationList();
var args = new PropertyChangedEventArgs(prop.Name);
foreach (Delegate dlg in delegates)
{
dlg.Method.Invoke(
dlg.Target,
new object[]
{
source,
args
});
}
doIfChanged();
}
}
Now you can use a strongly typed NotifyPropertyChanged declaration without using your base class
public class PersonStaticReflection : INotifyPropertyChanged
{
private string _firstName;
private string _lastName;
public string FirstName
{
get { return _firstName; }
set { this.SetProperty(
() => FirstName,
() => _firstName,
value);
}
}
public string LastName
{
get { return _lastName; }
set
{
this.SetProperty(() => LastName,
() => _lastName,
value,
() =>
{
// do something useful here
});
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
For those concerned, you can statically cache the PropertyChanged field in a dictionary mapped to the object type so that you only have to reflect on it one time.