DynamicMethod для ConstructorInfo.Invoke, что мне нужно учитывать?
У меня вопрос такой:
Если я собираюсь построить объект DynamicMethod, соответствующий вызову ConstructorInfo.Invoke, какие типы IL мне нужно реализовать, чтобы справиться со всеми (или большинством) типами аргументов, когда я могу гарантировать, что правильный тип и сколько аргументов будет передано до того, как я позвоню?
Фон
Я на третьей итерации моего контейнера IoC, и в настоящее время выполняю некоторое профилирование, чтобы выяснить, есть ли области, где я могу легко сэкономить много времени.
Одна вещь, которую я заметил, заключается в том, что при преобразовании в конкретный тип в конечном итоге я получаю вызываемый конструктор, используя ConstructorInfo.Invoke, передавая массив аргументов, который я разработал.
Что я заметил, так это то, что метод invoke имеет немного накладных расходов, и мне интересно, является ли большая часть этого просто различными реализациями тех же проверок, что и я.
Например, из-за кода соответствия конструктора, который у меня есть, чтобы найти соответствующий конструктор для предопределенных имен параметров, типов и значений, которые я передал, нет никакого способа, которым этот конкретный вызов invoke не закончится чем-то, что должно быть в состоянии справиться, как правильное количество аргументов, в правильном порядке, правильного типа и с соответствующими значениями.
При выполнении сеанса профилирования, содержащего миллион вызовов моего метода resolv, а затем заменив его реализацией DynamicMethod, которая имитирует вызов Invoke, время профилирования было таким:
- ConstructorInfo.Invoke: 1973мс
- DynamicMethod: 93ms
Это составляет около 20% от общего времени выполнения этого приложения профилирования. Другими словами, заменив вызов ConstructorInfo.Invoke на DynamicMethod, который делает то же самое, я могу сократить время выполнения на 20% при работе с базовыми сервисами с фабричной областью (т. Е. Все вызовы разрешения заканчиваются вызовом конструктора).
Я думаю, что это довольно существенно и требует более тщательного изучения того, сколько работы потребуется для создания стабильного генератора DynamicMethod для конструкторов в этом контексте.
Таким образом, динамический метод будет принимать массив объектов и возвращать построенный объект, и я уже знаю рассматриваемый объект ConstructorInfo.
Следовательно, похоже, что динамический метод будет состоять из следующего IL:
l001: ldarg.0 ; the object array containing the arguments
l002: ldc.i4.0 ; the index of the first argument
l003: ldelem.ref ; get the value of the first argument
l004: castclass T ; cast to the right type of argument (only if not "Object")
(repeat l001-l004 for all parameters, l004 only for non-Object types,
varying l002 constant from 0 and up for each index)
l005: newobj ci ; call the constructor
l006: ret
Есть ли что-то еще, что мне нужно рассмотреть?
Обратите внимание, что я знаю, что создание динамических методов, вероятно, будет недоступно при запуске приложения в "режиме ограниченного доступа" (иногда мозг просто не откажется от этих терминов), но в этом случае я легко могу обнаружить это и просто вызов первоначального конструктора, как и прежде, с накладными расходами и все.
2 ответа
Для типов значений шаг l004 должен быть l004: unbox.any T
,
Самый простой способ определить правильный IL, который вам нужно сгенерировать, - это посмотреть, что сгенерировано компилятором C#, используя некоторый тестовый код.
static void Test(object[] args)
{
TestTarget((string)args[0], (int)args[1], (DateTime?)args[2]);
}
static void TestTarget(string s, int i, DateTime? dt){}
компилируется в:
L_0000: ldarg.0 L_0001: ldc.i4.0 L_0002: ldelem.ref L_0003: строка литейного класса L_0008: ldarg.0 L_0009: ldc.i4.1 L_000a: ldelem.ref L_000b: unbox.any int32 L_0010: ldarg.0 L_0011: ldc.i4.2 L_0012: ldelem.ref L_0013: unbox.any [mscorlib] System.Nullable`1L_0018: вызов void Program:: TestTarget (string, int32, valuetype [mscorlib] System.Nullable`1 ) L_001d: ret
Доступны библиотеки, облегчающие (и ускоряющие) работу с отражением. Например, Fasterflect может генерировать IL для вызова любого конструктора - все, что вам нужно сделать, это передать ему аргументы, которые вы хотите использовать в конструкторе.
// note: class must have constructor with (int,string,string) signature
object obj = someType.CreateInstance( new { id=1, name="jens", foo="bar" } );
Библиотека также способна определить подходящий конструктор для вызова, если у вас нет набора параметров, которые точно соответствуют конструктору.
// try to map id, name and foo to constructor parameters
// allows changing the order and permit fallback to setting fields/properties
// e.g. might result in call to ctor(string,string) and set field "id"
object obj = someType.TryCreateInstance( new { id=1, name="jens", foo="bar" } );
Отказ от ответственности: я участвую в указанном проекте в качестве участника.