В последней версии .NET Framework среди новых возможностей было добавлено средства метапрограммирования под названием Expression Trees. На базе этой технологии, а именно основываясь на том принципе, что выражения на "обычном" языке программирования могут автоматически преобразовываться в синтаксические деревья, была разработана технология LINQ.
Но в этом посте речь пойдет о другой области применения возможности динамически собирать expression trees и компилировать их в работоспособный код. И эта область — оптимизация Reflection.
кросс-пост с персонального блога
Как известно, платой за гибкость при использовании рефлексии является производительность. Но в случае, когда она применяется к некому фиксированному набору метаданных, ее легко оптимизировать.
Но динамические Expression Trees предоставляют нам еще достаточно элегантный способ оптимизации. Суть его заключается в том, что для доступа к свойствам экземпляров известного класса мы будем генерировать соответствующий строго типизированный код в виде лямбда-функции, которая будет обращаться к ним напрямую и которую мы будем кэшировать для последующего повторного использования.
Сам по себе метод создания нужной лямбда-функции достаточно прост:
Если поставить в этом методе точку останова и посмотреть на строковое представление переменной getterExpression, то мы увидим, во что оно будет скомпилировано:

Обернем всю логику доступа к свойству класса в некий ReflectionHelper, который в дальнейшем можно будет расширить методами для вызова методов, инициализации свойств и т.д. Этот класс будет реализовывать метод GetPropertyValue следующим образом:
Для проверки того, насколько эта логика эффективна, разработаем небольшой тест:
Ну и результаты говорят сами за себя:

Как видим, такой способ оптимизации более чем жизнеспособен :)
Но в этом посте речь пойдет о другой области применения возможности динамически собирать expression trees и компилировать их в работоспособный код. И эта область — оптимизация Reflection.
кросс-пост с персонального блога
Как известно, платой за гибкость при использовании рефлексии является производительность. Но в случае, когда она применяется к некому фиксированному набору метаданных, ее легко оптимизировать.
Но динамические Expression Trees предоставляют нам еще достаточно элегантный способ оптимизации. Суть его заключается в том, что для доступа к свойствам экземпляров известного класса мы будем генерировать соответствующий строго типизированный код в виде лямбда-функции, которая будет обращаться к ним напрямую и которую мы будем кэшировать для последующего повторного использования.
Сам по себе метод создания нужной лямбда-функции достаточно прост:
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.
Если поставить в этом методе точку останова и посмотреть на строковое представление переменной getterExpression, то мы увидим, во что оно будет скомпилировано:

Обернем всю логику доступа к свойству класса в некий ReflectionHelper, который в дальнейшем можно будет расширить методами для вызова методов, инициализации свойств и т.д. Этот класс будет реализовывать метод GetPropertyValue следующим образом:
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.
Для проверки того, насколько эта логика эффективна, разработаем небольшой тест:
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.
Ну и результаты говорят сами за себя:

Как видим, такой способ оптимизации более чем жизнеспособен :)