Это оптимизация производительности для наиболее распространенных случаев. Имея отдельные перегрузки для общего числа параметров, им не нужно создавать аргумент массива params
, тем самым сохраняя распределение (хотя, возможно, все еще потребуется бокс, но это дешевле, чем массив). Теоретически перегрузки для параметров 0, 1, 2 и 3 не должны существовать, так как метод, который принимает params object[]
, также может обрабатывать их все. Это просто дороже.
dup
дублирует текущий элемент в стеке. stelem.ref
берет три элемента из стека, массив, индекс и значение для этого индекса массива, и сохраняет значение по индексу в массиве. Это означает, что ссылка на массив больше не будет в стеке впоследствии. Таким образом, dup
. Мы хотим сохранить ссылку на этот массив в верхней части стека, поскольку нам нужно передать его вызываемому методу, поэтому мы создаем массив, дублируем его, помещаем индекс и первый элемент, используем stelem.ref
для сохранения элемента в массив, и по-прежнему иметь ссылку на этот массив, который в противном случае был бы исчез.
Существуют альтернативные способы сделать это. Если вы возьмете код, который вы скопировали из декомпилированного C #, вы получите другой IL, где ссылка на массив берется из локальной переменной каждый раз:
IL_0036: ldc.i4.4
IL_0037: newarr [System.Private.CoreLib]System.Object
IL_003c: stloc.1
IL_003d: ldloc.1
IL_003e: ldc.i4.0
IL_003f: ldc.i4.1
IL_0040: box [System.Private.CoreLib]System.Int32
IL_0045: stelem.ref
IL_0046: ldloc.1
IL_0047: ldc.i4.1
IL_0048: ldc.i4.2
IL_0049: box [System.Private.CoreLib]System.Int32
IL_004e: stelem.ref
Я бы посчитал, что он менее эффективен, чем dup
, но, возможно, JIT на самом деле не волнует ни один из способов. Реальный декомпилированный код C # будет выглядеть примерно так:
string text = string.Format("{0}{1}{2}{3}", new object[] { 1, 2, 3, 4 });
, что приводит к тому же IL, что и
string text = string.Format("{0}{1}{2}{3}", 1, 2, 3, 4);