In the latest version of the .NET Framework, a new
metaprogramming tool called
Expression Trees has been added. Based on this technology, namely based on the principle that expressions in a "normal" programming language can be automatically converted into syntax trees, LINQ technology was developed.
But in this post we will discuss another area of ​​application, the ability to dynamically assemble expression trees and compile them into workable code. And this area is Reflection optimization.
cross post from personal blog
')
As you know, the price for flexibility when using reflection is performance. But when applied to a fixed set of metadata, it is easy to
optimize .
But dynamic Expression Trees provide us with a rather elegant way of optimization. Its essence lies in the fact that to access the properties of instances of a known class, we will generate the corresponding strictly typed code in the form of a lambda function that will directly access them and which we will cache for later reuse.
By itself, the method of creating the desired lambda function is quite simple:
private static Func < object , object > CreateGetter ( object entity, string propertyName)
{
var param = Expression.Parameter ( typeof ( object ), "e" );
Expression body = Expression.PropertyOrField (Expression.TypeAs (param, entity.GetType ()), propertyName);
var getterExpression = Expression.Lambda <Func < object , object >> (body, param);
return getterExpression.Compile ();
} * This source code was highlighted with Source Code Highlighter .
If we put a breakpoint in this method and look at the string representation of the getterExpression variable, we will see what it will compile into:
We will wrap all the logic of accessing a class property into a ReflectionHelper, which can later be extended with methods for calling methods, initializing properties, etc. This class will implement the GetPropertyValue method as follows:
readonly Dictionary <PropertyGetterKey, Func < object , object >> propertyGetters = new Dictionary <PropertyGetterKey, Func < object , object >> ();
public object GetPropertyValue ( object entity, string propertyName)
{
Func < object , object > getter;
var key = new PropertyGetterKey {Type = entity.GetType (), PropertyName = propertyName};
if (propertyGetters.ContainsKey (key))
getter = propertyGetters [key];
else
{
getter = CreateGetter (entity, propertyName);
propertyGetters.Add (key, getter);
}
return getter (entity);
} * This source code was highlighted with Source Code Highlighter .
To test how effective this logic is, we will develop a small test:
var entities = new List <Class1> ();
for ( var i = 0; i <20; i ++)
entities.Add ( new Class1 {Property1 = "Value" + i});
foreach ( var entity in entities)
{
var start = DateTime .Now.Millisecond;
var val = ReflectionHelper.Instance.GetPropertyValue (entity, "Property1" );
Console .WriteLine ( "{0} - {1}" , val, ( DateTime .Now.Millisecond - start));
} * This source code was highlighted with Source Code Highlighter .
Well, the results speak for themselves:
As you can see, this method of optimization is more than viable :)