Dumping Objects Using Expression Trees
No. I’m not proposing to get rid of objects.
A colleague of mine was asked if I knew a way to dump a list of objects of unknown type into a DataTable with better performance than the way he was using.
The objects being dumped usually have over a dozen of properties, but, for the sake of this post, let’s assume they look like this:
class SomeClass
{
public int Property1 { get; set; }
public long Property2 { get; set; }
public string Property3 { get; set; }
public object Property4 { get; set; }
public DateTimeOffset Property5 { get; set; }
}
The code he was using was something like this:
var properties = objectType.GetProperties();foreach (object obj in objects) { foreach (var property in properties) { property.GetValue(obj, null); } }
For a list of one million objects, this is takes a little over 6000 milliseconds on my machine.
I immediately thought: Expression Trees!
If the type of the objects was know at compile time, it would be something like this:
Expression<Func<SomeClass, int>> expression = o => o.Property1; var compiled = expression.Compile(); var propertyValue = compiled.Invoke(obj);
But, at compile time, the type of the object and, consequently, the type of its properties, is unknown. So, we’ll need, for each property, an expression tree like this:
Expression<Func<object, object>> expression = o => ((SomeClass)o).Property1;
The previous expression gets the value of a property of the conversion of the parameter of type object to the type of the object. The result must also be converted to type object because the type of the result must match the type of the return value of the expression.
For the same type of objects, the collection of property accessors would be built this way:
var compiledExpressions = (from property in properties let objectParameter = Expression.Parameter(typeof(object), "o") select Expression.Lambda<Func<object, object>>( Expression.Convert( Expression.Property( Expression.Convert( objectParameter, objectType ), property ), typeof(object) ), objectParameter ).Compile()).ToArray();
Looks bit overcomplicated, but reading all properties of all objects for the same object set with this code:
foreach (object obj in objects) { foreach (var compiledExpression in compiledExpressions) { compiledExpression (obj); } }
takes a little over 150 milliseconds on my machine.
That’s right. 2.5% of the previous time.