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

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

На некоторых .Net хостингах Expression использовать не получится, т.к. там не хватает привилегий коду для выполнения. (например не удается заюзать rsdn.data.framework.dll, на паре бесплатных хостингов). А так да, он быстр.
Спасибо, действительно так. Добавлю в минусы к этому способу.
Странно, сейчас попытался найти в MSDN про Sercurity по Expression<TDelegate>.Compile, и не нашел.

Если вдруг у кого есть информация, какой именно permission требует, напишите. Логически он должен требовать что-то…
Спасибо за статью! Давно хотел потестить скорость Expressions, да всё как-то руки не доходили :)
> var instance = Expression.Parameter(typeof(Object), «i»);
>…

вот это извращение.

а так не пробывали?
var expression = (Expression)( x => x > 0 );

и компилятор всё сделает за вас.
в 0) var expression = (Expression)( x => x > 0 ); не скомпилится, так что компилятор за вас ничего не сделает.
в 1) мне кажется вы не читали код, или не поняли что автор хотел сделать.
нужно иметь доступ к полю динамически, т.е. заранее неизвестно поле. напишите ка лямбду не зная значение чего вы хотите прочитать.
в 2) даже если знаете что прочитать заранее, установить значение таким способом не получится.
>в 0) var expression = (Expression)( x => x > 0 ); не скомпилится, так что компилятор за вас ничего не сделает.
Скомпилится. Парсер схавал скобки угловые:

(Expression<Func<int,bool>>)( x => x > 0 );
.
>в 1)
Да не посмотрел. Сыплю пепел на голову
Еще к слову, метод «Компиляция выражений (Expressions)» и «Словарь делегатов», одинаковы. Разница только в скорости доступа к словарю.

Магия цифр:
85,93% — 63,64% = 22,29%

Всё по тому что Expression.Compile() создает динамический метод и вызывает его через делегат. В итоге в обоих случаях получается делегат который вызывает компилированный код.
Действительно варианты похожие. Но вариант с Expression-ами:
а) дает все-таки некое подобие на строгую типизацию — мы задаем точный тип возврата.
б) не надо каждый раз писать руками делегат (он фактически генерируется).
Еще можно было рассмотреть вариант, аналогичный последнему, но написанный непосредственно на MSIL'е. Или в четвертом фреймворке Expression — это обертка, чтоб не писать опкоды?
А статья хорошая, спасибо.
э-э-э. При компиляции экспрешеннов получается MSIL-код. Что в 3.5, что в 4ом FW.
Да, это я понял. Я имел в виду, что возможно если сразу писать что-то типа:

//...
ILGenerator ctorIL = smth.GetILGenerator();

ctorIL.Emit(OpCodes.Ldarg_0);
ctorIL.Emit(OpCodes.Call, objCtor);
ctorIL.Emit(OpCodes.Ldarg_0);
ctorIL.Emit(OpCodes.Ldarg_1);
ctorIL.Emit(OpCodes.Stfld, xField); 
//...


то можно сэкономить еще немного времени.
На самом деле это был первый вариант реализации — с использованием DynamicMethod. Даже был более или менее работающий код, но он был довольно громоздкий, и не очень читабелен. А про производительности по сравнению с Expressions он давал разницу сравнимую с random (фактически автоматический генератор иногда генерирует непонятные конструкции по типа безусловных прыжков на следующую инструкцию). По этому я удалил это решение.

Возможно все-таки надо было включить, но статья и так вышла слегка раздутая, хотелось чтоб различия в методах были все-таки более заметные. А то реально можно придумать еще парочку модификаций каждого метода :)
Отличная статья, приятный стиль изложения. Но проверьте пожалуйста текст на орфографию — очень режет глаза в некоторых местах: «по этому» например. Думаю вы просто спешили, когда набирали — ничего страшного со всеми случается :)
Огромное спасибо! Действительно, очень хотелось все-таки выложить статью. Думаю время публикации «05:44» чуть чуть меня оправдает. Да и орфография — одно из самый слабых моих качеств, но стараюсь импрувить :)

Конечно же проверю все и постараюсь исправлю.
Я бы еще при компиляции деревьев выражений убрал приведение типа. Пусть был бы тип делегата Func<StaticObject, T>.
Без приведения с проверкой будет работать быстрее.
Действительно так, но я хотел метод с выражениями максимально приблизить к рефлексии. PropertyInfo.GetValue тоже принимает Object как первый параметр.
Да, принимает object, но смысл кода в статической типизации (которая генерируется динамически, конечно).
В реальной жизни я использую именно деревья выражений — самый эффективный и правильный способ, на мой взгляд.
В реальной системе метод «Динамический объект с перехватом вызова» фактически сведется к методу Рефлексии т.к. в перехватчике TryMember надо будет на основе рефлексии определять что дергать.

Метод с ExpandoObject мало пригодный для разработки объектов с динамическими методами и свойстваи т.к. код динамических методов надо назначать экземплярам в рантайме, это неудобно. (или я не правильно понял идею ExpandoObject ?)

В разрабтываемой платформе с динамической типизацией мы сейчас используем рефлексию с кешированием. Классы разрабатываются программистами как статические, а динамические свойста и методы размечаются атрибута, данные для рефлексии кешируются.

После перехода на .net 4.0 планируем перейти на метод «Динамический объект со статическим контейнером», это действительно дает большой прирост производительности в сравнении с рефлексией
В реальной системе перехватом можно пользоваться для совсем не существующих методов. Например: есть веб-узел, предоставляющий свои функции через урл по типу domain.com/{funcname}/{parameters}
С помощью перехвата это можно сделать реализовав один перехватчик кидающий запрос на узел. В результате будет что-то вроде service.DoSomeFunc(a,b) будет кидать запрос на domain/DoSomeFunc/a=xxx&b=xxx

С ExpandoObject — действительно он лучше всего предназначен для представления сильно отличающихся друг от друга сущностей. Иначе стоит задуматься о создании общих классов с необходимыми функциями и делегировать им поведение.

Если сравнивать с рефлексией, то метод с выражениями меня лично очень поразил — ведь фактически особых изменений нет — вместо PropertyInfo/MethodInfo храним откомпилированное выражение. А прирос производительности просто потрясающий.
А как будет выглядить вызов методово в случае с методом на основе Expressions?
Т.е. если вместо свойства (property) — метод? тогда просто вместо Expression.Property — Expression.Call. А остальное то же самое — в конце концов выходит обычный Func<>, который можно вызывать, и по скорости намного выше MethodInfo.Invoke

Кстати, если говорить именно о 4.0, я там заметил RuntimeVariablesExpression — возможно его можно было бы использовать, но пока не нашел достаточно информации.
leo, чисто ради интереса написал вот такой метод:
public static Action MakeCall<TClass, TArg>(TClass instance, string name)
{
var instanceExpr = Expression.Constant(instance);
var param = Expression.Parameter(typeof (TArg), "arg");
var call = Expression.Call(instanceExpr, name, Type.EmptyTypes, param);
var expr = Expression.Lambda<Action>(call,param).Compile();
return expr;
}

использовать:
сорри, случайно отправилось.

теперь вместо
var method = typeof (B).GetMethod("DoWork");
method.Invoke(b, "xxx");

Можно
var b = new B();
var expr = MakeCall<B,string>(b, "DoWork");
expr("xxx");


разница (пустой класс б с 1 методом, фор с вызовом 20000000 раз через expr и через method.Invoke(b, «xxx»); ):
time = 00:00:00.8665175
time = 00:00:27.2921866

впечатляет… :)
Большое спасибо за подробный анализ, очень познавательно, только:

РЕФЛЕКСИЯ, -и; ж. [от лат. reflexio — обращение назад] Книжн. Размышление о своих чувствах, анализ своих переживаний. Предаваться рефлексии. Обнаружить склонность к рефлексии. //Осмысление чего-л., размышление над чем-л. Р. пьесы. Р. над языковым материалом.

Всё-таки в данном контексте «reflection» — это «отражение».
Когда я писал, у меня тоже возник этот вопрос. На википедии было три варианта: «отражение или рефлексия (синоним интроспекция, англ. reflection)». Я выбрал «рефлексия», потому-что оно проще чем «интроспекция» но более ближе к английскому варианту «reflection».

Но, действительно, в дальнейшем я все-таки пересмотрю свою точку зрения. Спасибо.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории