就这样!在将OldValue添加到Changes之后,基本上,声明一个新的局部变量,然后push检索该值并将其弹出到该局部变量中。
private static Func<T, T, List<Change>> GenerateDiffer()
{
var dm = new DynamicMethod("DoDiff", typeof(List<Change>), new Type[] { typeof(T), typeof(T) }, true);
var il = dm.GetILGenerator();
// change list
il.DeclareLocal(typeof(List<Change>));
il.DeclareLocal(typeof(Change));
il.DeclareLocal(typeof(object)); // boxed new value
il.DeclareLocal(typeof(object)); // RM - boxed old value
il.Emit(OpCodes.Newobj, typeof(List<Change>).GetConstructor(Type.EmptyTypes));
// [list]
il.Emit(OpCodes.Stloc_0);
foreach (var prop in RelevantProperties())
{
//[]
il.Emit(OpCodes.Ldarg_0);
//[original]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
//[original prop val]
/*
* RM - We're going to dupe and store the old value into loc3.
*/
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_3);
// [original prop val, current prop val]
/*
*
*/
il.Emit(OpCodes.Ldarg_1);
// [original prop val, current]
il.Emit(OpCodes.Callvirt, prop.GetGetMethod(true));
// [original prop val, current prop val]
il.Emit(OpCodes.Dup);
// [original prop val, current prop val, current prop val]
if (prop.PropertyType != typeof(string))
{
il.Emit(OpCodes.Box, prop.PropertyType);
// [original prop val, current prop val, current prop val boxed]
}
il.Emit(OpCodes.Stloc_2);
// [original prop val, current prop val]
il.EmitCall(OpCodes.Call, typeof(Snapshot<T>).GetMethod("AreEqual", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(new Type[] { prop.PropertyType }), null);
// [result]
Label skip = il.DefineLabel();
il.Emit(OpCodes.Brtrue_S, skip);
// []
il.Emit(OpCodes.Newobj, typeof(Change).GetConstructor(Type.EmptyTypes));
// [change]
il.Emit(OpCodes.Dup);
// [change,change]
il.Emit(OpCodes.Stloc_1);
// [change]
il.Emit(OpCodes.Ldstr, prop.Name);
// [change, name]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_Name"));
// []
/*
* Begin setting value
*/
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_3);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_OldValue"));
// []
/*
* End Playground
*/
il.Emit(OpCodes.Ldloc_1);
// [change]
il.Emit(OpCodes.Ldloc_2);
// [change, boxed]
il.Emit(OpCodes.Callvirt, typeof(Change).GetMethod("set_NewValue"));
// []
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ldloc_1);
// [change list, change]
il.Emit(OpCodes.Callvirt, typeof(List<Change>).GetMethod("Add"));
// []
il.MarkLabel(skip);
}
il.Emit(OpCodes.Ldloc_0);
// [change list]
il.Emit(OpCodes.Ret);
return (Func<T, T, List<Change>>)dm.CreateDelegate(typeof(Func<T, T, List<Change>>));
}