Pull to refresh

Comments 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».

Но, действительно, в дальнейшем я все-таки пересмотрю свою точку зрения. Спасибо.
Sign up to leave a comment.

Articles