Это интересный вопрос, но, к сожалению, ваш подход к «обману» системы не повысит эффективность вашей программы. Если бы это было возможно, компилятор мог бы сделать это для нас относительно легко!
Вы правы, что при вызове IList<T>
через ссылку на интерфейс методы отправляются во время выполнения и, следовательно, не могут быть встроены. Поэтому вызовы IList<T>
методов, таких как Count
и индексатор, будут вызываться через интерфейс.
С другой стороны, это неправда, что вы можете добиться какого-либо преимущества в производительности (по крайней мере, с помощью текущего компилятора C # и .NET4 CLR), переписав его как универсальный метод.
Почему бы и нет? Сначала немного предыстории. Работа C # generics заключается в том, что компилятор компилирует ваш универсальный метод, который имеет заменяемые параметры, а затем заменяет их во время выполнения фактическими параметрами. Это вы уже знали.
Но параметризованная версия метода знает о типах переменных не больше, чем мы с вами во время компиляции. В этом случае все, что знает компилятор о Foo2
, это то, что list
является IList<int>
. Мы имеем ту же информацию в родовом Foo2
, что и в необщем Foo1
.
На самом деле, чтобы избежать раздувания кода, JIT-компилятор создает только один экземпляр универсального метода для всех ссылочных типов . Вот документация Microsoft , которая описывает эту замену и создание экземпляра:
Если клиент указывает ссылочный тип, то JIT-компилятор заменяет общие параметры в IL сервера на Object и компилирует его в собственный код. Этот код будет использоваться в любом последующем запросе ссылочного типа вместо параметра универсального типа. Обратите внимание, что таким образом JIT-компилятор использует только реальный код. Экземпляры по-прежнему распределяются в соответствии с их размером из управляемой кучи, и преобразование не выполняется.
Это означает, что версия метода JIT-компилятора (для ссылочных типов) является небезопасной , но это не имеет значения, поскольку компилятор обеспечил безопасность всех типов во время компиляции. Но что еще более важно для вашего вопроса, нет возможности выполнить встраивание и повысить производительность.
Редактировать: Наконец, эмпирически, я только что выполнил тестирование Foo1
и Foo2
, и они дают идентичные результаты производительности. Другими словами, Foo2
это не чуть быстрее, чем Foo1
.
Давайте добавим «встроенную» версию Foo0
для сравнения:
int Foo0(List<int> list)
{
int sum = 0;
for (int i = 0; i < list.Count; ++i)
sum += list[i];
return sum;
}
Вот сравнение производительности:
Foo0 = 1719
Foo1 = 7299
Foo2 = 7472
Foo0 = 1671
Foo1 = 7470
Foo2 = 7756
Итак, вы можете видеть, что Foo0
, который может быть встроен, значительно быстрее, чем два других. Вы также можете увидеть, что Foo2
немного медленнее, чем Foo0
.
.