Открыть список
Как стать автором
Обновить

Expression Trees и оптимизация Reflection

.NET
В последней версии .NET Framework среди новых возможностей было добавлено средства метапрограммирования под названием Expression Trees. На базе этой технологии, а именно основываясь на том принципе, что выражения на "обычном" языке программирования могут автоматически преобразовываться в синтаксические деревья, была разработана технология LINQ.

Но в этом посте речь пойдет о другой области применения возможности динамически собирать 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.


Ну и результаты говорят сами за себя:



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

Теги:.net framework 3.5reflectionexpression treeslinq
Хабы: .NET
Всего голосов 38: ↑29 и ↓9 +20
Просмотры5.5K

Комментарии 29

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Похожие публикации

.Net Fullstack Developer (Mid-level)
от 1 000 до 2 000 $SNAPIOМожно удаленно
.net developer
от 100 000 до 200 000 ₽БАРС ГрупКазаньМожно удаленно
.Net разработчик
от 120 000 ₽ITFB GroupМосква
.NET программист
от 80 000 до 150 000 ₽ByndyusoftЧелябинск
Разработчик .NET / C#
от 90 000 до 150 000 ₽nopCommerceЯрославль

Лучшие публикации за сутки