Ответ 1
Я предполагаю, что вы обнаружили незначительную ошибку в оптимизаторе. Для массивов есть всевозможные специальные коды. Спасибо, что привлек мое внимание.
Несколько дней назад, написав ответ на этот вопрос здесь, при переполнении, я немного удивился компилятору С#, который не делал то, что я ожидал от него делать. Посмотрите на следующие фрагменты кода:
Во-первых:
object[] array = new object[1];
for (int i = 0; i < 100000; i++)
{
ICollection<object> col = (ICollection<object>)array;
col.Contains(null);
}
Во-вторых:
object[] array = new object[1];
for (int i = 0; i < 100000; i++)
{
ICollection<object> col = array;
col.Contains(null);
}
Единственное различие в коде между двумя фрагментами - это передача в ICollection <object> . Поскольку объект [] явно реализует интерфейс ICollection <object> , я ожидал, что два фрагмента будут скомпилированы до одного и того же IL и поэтому будут идентичными. Тем не менее, при выполнении тестов производительности на них я заметил, что последний был примерно в 6 раз быстрее прежнего.
После сравнения IL из обоих фрагментов я заметил, что оба метода были идентичны, за исключением инструкции IL castclass в первом фрагменте.
Удивленный этим, я теперь задаюсь вопросом, почему компилятор С# не является "умным" здесь. Вещи никогда не такие простые, как кажется, поэтому почему компилятор С# здесь немного наивен?
Я предполагаю, что вы обнаружили незначительную ошибку в оптимизаторе. Для массивов есть всевозможные специальные коды. Спасибо, что привлек мое внимание.
Это грубое предположение, но я думаю, что это связано с отношением Array к его универсальному IEnumerable.
В .NET Framework версии 2.0 Класс Array реализует System.Collections.Generic.IList, System.Collections.Generic.ICollection, а также System.Collections.Generic.IEnumerable общие интерфейсы. реализации предоставляются массивы во время выполнения и, следовательно, не видимый для сборки документации инструменты. В результате общий интерфейсы не отображаются в синтаксис объявления для массива класса, и нет ссылки темы для членов интерфейса, которые доступный только путем литья массива в общий тип интерфейса (явный реализация интерфейса). Ключ что нужно знать, когда вы бросаете массив к одному из этих интерфейсов члены, которые добавляют, вставляют или удалять элементы NotSupportedException.
См. Статья MSDN.
Неясно, относится ли это к .NET 2.0+, но в этом специальном случае было бы совершенно понятно, почему компилятор не может оптимизировать ваше выражение, если оно становится действительным только во время выполнения.
Это не похоже на просто упущенную возможность в компиляторе подавить подачу. Он будет работать, если вы напишете это следующим образом:
ICollection<object> col = array as ICollection<object>;
который предполагает, что он становится слишком консервативным, потому что броски могут бросать исключения. Тем не менее, он работает, когда вы бросаете не-общий ICollection. Я бы сделал вывод, что они просто упустили это.
Там больше проблема оптимизации при работе здесь, JIT-компилятор не применяет оптимизацию подъема цикла. Он должен был переписать код следующим образом:
object[] array = new object[1];
ICollection<object> col = (ICollection<object>)array;
for (int i = 0; i < 100000; i++)
{
col.Contains(null);
}
Какая стандартная оптимизация в генераторе кода C/С++, например. Тем не менее, оптимизатор JIT не может записывать много циклов в виде анализа, необходимого для обнаружения таких возможных оптимизаций. Радость в этом заключается в том, что оптимизированный управляемый код по-прежнему довольно отлаживаемый. И что все еще есть роль для программиста С# для написания кода исполнения.