Created
May 10, 2011 00:27
-
-
Save rally25rs/963715 to your computer and use it in GitHub Desktop.
Code Sample 1 (DynamicMethods and ILEmit)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* *** | |
* 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