Introduction
Quite often, I run across the need to dynamically retrieve the value of an object's property (or field), where the actual member might not be known until run-time.
Some examples of where one might use this are:
- Comparer for sorting an array/list/collection
- Class to filter a collection based on member values
- Class to serialize/deserialize a list of objects (faster than
BinaryFormatter
)
Background
In the past, I have made use of run-time reflection to: find a property's PropertyInfo
or a field's FieldInfo
, and Invoke
the GetValue
method of the PropertyInfo
or FieldInfo
on an object to retrieve the value.
PropertyInfo pi = typeof(Widget).GetProperty("ID");
Int32 id = (Int32) pi.GetValue(widget, null);
This works fine if you only have to retrieve the member's value for a single object; but once you have to get the value in a loop, it performs very slow.
The New Method (.NET 2.0)
I got to thinking, why not generate (at run-time) the same IL code that the compiler generates at compile time, and use that to call the property's get
accessor or retrieve the field's value?
In previous versions of .NET (1.0 and 1.1), there is a pretty big overhead to generating IL code. This is because using the System.Reflection.Emit
namespace requires an out-of-process call to csc.exe to actually generate the IL Code.
Thankfully, .NET 2.0 provides us with a new DynamicMethod
class, that is a much more light-weight method of generating IL code.
Using this new DynamicMethod
class, it is easy to produce a fast method for dynamically retrieving member values.
Usage
TypeUtility<Widget>.MemberGetDelegate<Int32>
GetID = TypeUtility<Widget>.GetMemberGetDelegate<Int32>("ID");
Int32 id = GetID(widget);
The Code
public class TypeUtility<ObjectType>
{
public delegate MemberType
MemberGetDelegate<MemberType>(ObjectType obj);
public static MemberGetDelegate<MemberType>
GetMemberGetDelegate<MemberType>(string memberName)
{
Type objectType = typeof(ObjectType);
PropertyInfo pi = objectType.GetProperty(memberName);
FieldInfo fi = objectType.GetField(memberName);
if (pi != null)
{
MethodInfo mi = pi.GetGetMethod();
if (mi != null)
{
return (MemberGetDelegate<MemberType>)
Delegate.CreateDelegate(typeof(
MemberGetDelegate<MemberType>), mi);
}
else
throw new Exception( String.Format(
"Property: '{0}' of Type: '{1}' does" +
" not have a Public Get accessor",
memberName, objectType.Name ) );
}
else if (fi != null)
{
DynamicMethod dm = new DynamicMethod("Get" + memberName,
typeof(MemberType), new Type[] { objectType }, objectType);
ILGenerator il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, fi);
il.Emit(OpCodes.Ret);
return (MemberGetDelegate<MemberType>)
dm.CreateDelegate(typeof(MemberGetDelegate<MemberType>));
}
else
throw new Exception( String.Format(
"Member: '{0}' is not a Public Property or Field of Type: '{1}'",
memberName, objectType.Name ));
}
}
Tweaking Performance - Caching the Generated Code
There is a small amount of build up time that occurs during each call to GetMemberGetDelegate
. This is due to having to generate the IL code and create a delegate. If possible, the generated delegate should be kept around for future use. Below is the code that can be used for that purpose.
public class TypeUtility<ObjectType>
{
private static Dictionary<string,Delegate>
_memberGetDelegates = new Dictionary<string,Delegate>();
public static MemberGetDelegate<MemberType>
GetCachedMemberGetDelegate<MemberType>(string memberName)
{
if ( _memberGetDelegates.ContainsKey(memberName) )
return (MemberGetDelegate<MemberType>)
_memberGetDelegates[memberName];
MemberGetDelegate<MemberType> returnValue =
GetMemberGetDelegate<MemberType>( memberName );
lock ( _memberGetDelegates )
{
_memberGetDelegates[memberName] = returnValue;
}
return returnValue;
}
}
Performance Test Results
Based on my performance tests, using the dynamically generated code is 30x-50x faster than using normal reflection. It is still only about half as fast as using compiled code, which I imagine is due to the number of method calls. With compiled code, there is only one method call: the property's get
accessor. When using the run-time generated code, you end up making two method calls: first to the generated code, and second to the property's get
accessor. Depending on your exact usage, you may be able to optimize further and get back to one method call.
An Example Using This Concept
I mentioned earlier that one example of where this concept could come in handy is in the case of a generic comparer used to sort a collection or a list.
Consider the following:
List<Widget> widgets = GetWidgetsListFromSomewhere();
widgets.Sort( new MemberComparer<Widget>("ID") );
widgets.Sort( new MemberComparer<Widget>("Name") );
You can see how easy this makes it to sort your collection based on a dynamic property or field. For example, you could easily wire this up to a UI to allow for fast sorting based on the column a user clicked on.
Here is the code for a simple MemberComparer
class:
Note: This could easily be extended to support chaining of fields and sort directions together.
public class MemberComparer<ObjectType> : IComparer<ObjectType>
{
private delegate int CompareDelegate(ObjectType x, ObjectType y);
private CompareDelegate _compare;
public MemberComparer(string memberName)
{
_compare = GetCompareDelegate(memberName);
}
public int Compare(ObjectType x, ObjectType y)
{
return _compare(x, y);
}
private CompareDelegate GetCompareDelegate(string memberName)
{
Type objectType = typeof(ObjectType);
PropertyInfo pi = objectType.GetProperty(memberName);
FieldInfo fi = objectType.GetField(memberName);
Type memberType = null;
bool isProperty = false;
if (pi != null)
{
if (pi.GetGetMethod() != null)
{
memberType = pi.PropertyType;
isProperty = true;
}
else
throw new Exception(String.Format(
"Property: '{0}' of Type: '{1}' " +
"does not have a Public Get accessor",
memberName, objectType.Name));
}
else if (fi != null)
memberType = fi.FieldType;
else
throw new Exception(String.Format(
"'{0}' is not a Public Field or Property" +
" with a Get accessor for Type: '{1}' ",
memberName, objectType.Name));
Type comparerType =
typeof(Comparer<>).MakeGenericType(
new Type[] { memberType });
MethodInfo getDefaultMethod =
comparerType.GetProperty("Default").GetGetMethod();
MethodInfo compareMethod =
getDefaultMethod.ReturnType.GetMethod("Compare");
DynamicMethod dm =
new DynamicMethod("Compare_" + memberName, typeof(int),
new Type[] { objectType, objectType }, comparerType);
ILGenerator il = dm.GetILGenerator();
il.EmitCall(OpCodes.Call, getDefaultMethod, null);
il.Emit(OpCodes.Ldarg_0);
if (isProperty)
il.EmitCall(OpCodes.Callvirt, pi.GetGetMethod(), null);
else
il.Emit(OpCodes.Ldfld);
il.Emit(OpCodes.Ldarg_1);
if (isProperty)
il.EmitCall(OpCodes.Callvirt, pi.GetGetMethod(), null);
else
il.Emit(OpCodes.Ldfld);
il.EmitCall(OpCodes.Callvirt, compareMethod, null);
il.Emit(OpCodes.Ret);
return (CompareDelegate)dm.CreateDelegate(
typeof(CompareDelegate));
}
}
Assuming you want to sort the Widget
class, the CompareDelegate
that gets dynamically created contains the dynamic equivalent of the following code:
public int Compare(Widget x, Widget y)
{
return System.Collections.Generic.Comparer<string>.Default.Compare(
x.Name, y.Name);
}
public int Compare(Widget x, Widget y)
{
return System.Collections.Generic.Comparer<int>.Default.Compare(x.ID, y.ID);
}
Conclusion
There are many other places that this concept can be used in place of the old slow method of reflection. With this article, I have only scratched the surface of what can be done. Hopefully, I have at least given some insight into how powerful Reflection.Emit
can be. I leave it up to the reader, however, to find other places that they might be able to use similar code and/or concepts.
History
- 07/04/2006 - Updated code to also work with fields, and added a generic comparer example.
- 06/23/2006 - Created initial version.