Skip to content

Instantly share code, notes, and snippets.

@rally25rs
Created May 10, 2011 00:27
Show Gist options
  • Save rally25rs/963715 to your computer and use it in GitHub Desktop.
Save rally25rs/963715 to your computer and use it in GitHub Desktop.
Code Sample 1 (DynamicMethods and ILEmit)
/* ***
* This is a method from an object-relation-mapping (ORM) tool that I had started to write.
* I eventually stopped working on it, ad instead contributed time to the open source
* SubSonic project instead.
*
* This method is an example of generating DynamicMethods to be used at runtime to check and
* set some properties any an arbitrary type. This was used over straight reflection every time
* because reflection can be fairly slow. The use of a DynamicMethod is much quicker.
* *** */
/// <summary>
/// This method builds a pair of <see cref="DynamicMethod"/>s that can be used to save off
/// the current values of the mapped properties, and later compare them to the current
/// property values to see if any have changed. The values are stored in an array and are
/// always stored in the same order, that way we don't have to use the overhead of a Hash,
/// and keeps the saveValues and checkValues methods at O(n) where n is the numebr of
/// mapped properties.
/// </summary>
private void GenerateDynamicMethods()
{
if (this._stormTrackedPropertyValues == null)
this._stormTrackedPropertyValues = new List<object>();
// generate dynamic methods to save and check all property values.
// this used to be done by reflection, but this is much faster...
this.saveValues = new DynamicMethod("", null, new Type[] { this.GetType() }, this.GetType());
this.checkValues = new DynamicMethod("", typeof(bool), new Type[] { this.GetType() }, this.GetType());
ILGenerator sv_il = this.saveValues.GetILGenerator();
ILGenerator cv_il = this.checkValues.GetILGenerator();
Label cv_retTrueLabel = cv_il.DefineLabel();
MethodInfo get_values = typeof(StormMapped).GetProperty("_stormTrackedPropertyValues", BindingFlags.Instance | BindingFlags.NonPublic).GetGetMethod(true);
MethodInfo add_value = this._stormTrackedPropertyValues.GetType().GetMethod("Add");
MethodInfo get_value = this._stormTrackedPropertyValues.GetType().GetMethod("get_Item");
int propertyNumber = 0;
ClassLevelMappedAttribute[] classAttribArr = this.GetType().GetCachedAttributes<ClassLevelMappedAttribute>(true);
foreach (ClassLevelMappedAttribute classAttrib in classAttribArr)
{
if ((classAttrib.SupressEvents & (StormPersistenceEvents.Insert | StormPersistenceEvents.Update)) != (StormPersistenceEvents.Insert | StormPersistenceEvents.Update))
{
// Do NOT itterate through ClassLevelMappedAttribute.PropertyAttributes here.
// If we call this method from the constructor, then that collection will not
// have been populated yet, because it happens in the Validate methods, which
// aren't run untill first load/persist.
foreach (PropertyInfo pi in this.GetType().GetProperties())
{
PropertyLevelMappedAttribute[] propAttribArr = pi.GetCachedAttributes<PropertyLevelMappedAttribute>(true);
foreach (PropertyLevelMappedAttribute propAttrib in propAttribArr)
{
if (!(propAttrib is StormRelationMappedAttribute) && (classAttrib.SupressEvents & (StormPersistenceEvents.Insert | StormPersistenceEvents.Update)) != (StormPersistenceEvents.Insert | StormPersistenceEvents.Update))
{
/* *** IL to save property values *** */
// load 'this._stormTrackedPropertyValues' onto the stack
sv_il.Emit(OpCodes.Ldarg_0);
sv_il.Emit(OpCodes.Call, get_values);
// get the value of the property onto the stack
sv_il.Emit(OpCodes.Ldarg_0);
sv_il.Emit(OpCodes.Call, pi.GetGetMethod(true));
// box a value type into a ref type if needed
// ex: int -> Int32, bool -> Boolean, etc...
if (pi.PropertyType.IsValueType)
sv_il.Emit(OpCodes.Box, pi.PropertyType);
// add the return value from the property's getter into the value list
sv_il.Emit(OpCodes.Callvirt, add_value);
/* *** IL to check property values *** */
// load 'this._stormTrackedPropertyValues' onto the stack
cv_il.Emit(OpCodes.Ldarg_0);
cv_il.Emit(OpCodes.Call, get_values);
// load the value out of the saved values array onto the stack
cv_il.Emit(OpCodes.Ldc_I4, propertyNumber);
cv_il.Emit(OpCodes.Callvirt, get_value);
// unbox value
if (pi.PropertyType.IsValueType)
cv_il.Emit(OpCodes.Unbox_Any, pi.PropertyType);
// get the value of the property onto the stack
cv_il.Emit(OpCodes.Ldarg_0);
cv_il.Emit(OpCodes.Call, pi.GetGetMethod(true));
// see if the values are equal. if not, branch to label.
cv_il.Emit(OpCodes.Ceq); // puts int value 0 or 1 on the stack
cv_il.Emit(OpCodes.Brfalse, cv_retTrueLabel);
propertyNumber++;
}
}
}
}
}
// return void
sv_il.Emit(OpCodes.Ret);
// return false
cv_il.Emit(OpCodes.Ldc_I4_0);
cv_il.Emit(OpCodes.Ret);
// return true;
cv_il.MarkLabel(cv_retTrueLabel);
cv_il.Emit(OpCodes.Ldc_I4_1);
cv_il.Emit(OpCodes.Ret);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment