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" } );

Отказ от ответственности: я участвую в указанном проекте в качестве участника.

Другие вопросы по тегам