using System.Diagnostics;
using System.Collections.Generic;
using System.Linq.Expressions;
public static void Main()
Console.WriteLine(poco.HasChangedSlow("Prop1"));
Console.WriteLine(poco.HasChangedSlow("Prop2"));
var watch = new Stopwatch();
Console.WriteLine(watch.ElapsedTicks);
Console.WriteLine(watch.ElapsedTicks);
public static void ThousandSlowTests(POCO poco)
poco.Prop1 = 0.ToString();
for (var i = 0; i<1000; i++)
poco.Prop1 = i.ToString();
poco.HasChangedSlow("Prop1");
public static void ThousandFastTests(POCO poco)
poco.Prop1 = 0.ToString();
for (var i = 0; i<1000; i++)
poco.Prop1 = i.ToString();
poco.HasChangedFast("Prop1");
public class POCO : HasChangedBase
public string Prop1 {get;set;}
public string Prop2 {get;set;}
public class HasChangedBase
private class PropertyState
public PropertyInfo Property { get; set; }
public object Value { get; set; }
public Func<object, object> Getter { get; set; }
private Dictionary<string, PropertyState> propertyStore;
var getter = HasChangedBase.BuildUntypedGetter(p);
public bool HasChangedFast(string propertyName)
return propertyStore != null
&& propertyStore.ContainsKey(propertyName)
&& propertyStore[propertyName].Value != propertyStore[propertyName].Getter(this);
public bool HasChangedSlow(string propertyName)
return propertyStore != null
&& propertyStore.ContainsKey(propertyName)
&& propertyStore[propertyName].Value != propertyStore[propertyName].Property.GetValue(this);
public static Func<object, object> BuildUntypedGetter(PropertyInfo propertyInfo)
var targetType = propertyInfo.DeclaringType;
var methodInfo = propertyInfo.GetGetMethod();
var returnType = methodInfo.ReturnType;
var exTarget = Expression.Parameter(typeof(object), "t");
var exBody = Expression.Call(Expression.Convert(exTarget, targetType), methodInfo);
var exBody2 = Expression.Convert(exBody, typeof(object));
var lambda = Expression.Lambda<Func<object, object>>(exBody2, exTarget);
var action = lambda.Compile();