Monday, June 15, 2009

More Static Reflection

Today we are going to speak about an implementation of the approach describe in this post.

INotifyPropertyChanged is responsible for notifying clients that a property value has changed.To implement INotifyPropertyChanged you should write a code similar to

public class Client : INotifyPropertyChanged
{
private string name;


public string Name
{
get { return name; }

set
{
if (name == value)
return;
name = value;
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
}


and the test of the behaviour that we want would be



[Test]
public void Notification_Event_Will_Raized_On_Property_Change()
{
var client = new Client();
bool raized = false;
client.PropertyChanged += (sender, args) => raized = true;

client.Name = "name";

Assert.AreEqual(true, raized);
}


but we can replace that ugly setter of Name property with a more strongly typed and elegant code using static reflection technic .



here are the extension methods that will help us



public static class ReflectionExtensions
{

public static void SetProperty<T>(this INotifyPropertyChanged source,
Expression<Func<T>> propExpr,
ref T propertyValueHolder,
T value, Action doIfChanged) where T : class
{
var prop = (PropertyInfo)((MemberExpression)propExpr.Body).Member;
var currVal = (T)prop.GetValue(source, null);
if (currVal == null && value == null)
return;
if (currVal == null || !currVal.Equals(value))
{
propertyValueHolder = value;
var eventDelegate = (MulticastDelegate)source.GetType().GetField("PropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic).
GetValue(source);
if (eventDelegate != null)
{
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();
}
}
public static void SetProperty<T>(this INotifyPropertyChanged source,
Expression<Func<T>> propExpr,
ref T propertyValueHolder,
T value) where T : class
{
source.SetProperty(propExpr, ref propertyValueHolder, value, () => { });
}


}


now we can replace our ugly setter with the SetProperty extension method and our test will continue to pass



private string name;


public string Name
{
get { return name; }

set
{

this.SetProperty(()=>Name,ref name,value);
}
}


You see?? Now our property does not contain any string or duplicate code



the only thing that concern me is what happen with the speed? are all that lamdas and expression trees going to slow down my code?



lets write some tests on this one also



[Test]
public void Notification_Event_Will_Raized_On_Property_Change()
{
var client = new Client();
bool raized = false;
client.PropertyChanged += (sender, args) => raized = true;
var stopwatch=new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 1000; i++)
client.Name = "name" + i;
long ticks = stopwatch.ElapsedMilliseconds;
stopwatch=new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 1000; i++)
client.NameWithSR = "name" + i;

Assert.AreApproximatelyEqual(ticks, stopwatch.ElapsedMilliseconds,ticks*30);
}


damn!!! almost 30 times slower I have to be careful with those lambas although for most apps I mean for those that do not insert thousands of records at one go that speed is more than acceptable



Of course one can speed that code more by moving those extension methods to a base class and creating a cached of PropertyChanged

Subscribe to XAF feed
Subscribe to community feed

DiggIt!

0 comments:

Post a Comment