Что такое магия массивов в C # - PullRequest
22 голосов
/ 20 декабря 2010
int[] a = new int[5];
string[] b = new string[1];

Типы a и b наследуются от абстрактного System.Array, но во встроенной библиотеке нет реальных классов (кажется, что есть некоторые типы времени выполнения, вы не можете найти класс определения типа int[]). Можете ли вы сказать мне, что происходит во время компиляции? И почему они (команда c #) создали этот дизайн (я имею в виду, почему это не что-то вроде Array<T>, вместо этого они используют абстрактный класс с магией компилятора)?

Ответы [ 8 ]

18 голосов
/ 20 декабря 2010

Попытка объяснить это с помощью системы типов .NET не продвинет вас слишком далеко. В компилятор JIT и CLR встроена поддержка ядра для создания массивов. Заявление, подобное этому:

        var arr = new int[5];

Генерирует этот IL:

  IL_0001:  ldc.i4.5
  IL_0002:  newarr     [mscorlib]System.Int32

Что компилятор JIT затем переводит в этот машинный код:

00000035  mov         edx,5                 ; arg2 = array size
0000003a  mov         ecx,6F535F06h         ; arg1 = typeof(int)
0000003f  call        FFD52128              ; call JIT_NewArr1(type, size)

Основными компонентами здесь являются выделенный код операции IL, newarr , вместо обычного кода операции newobj , который создает экземпляр класса. И простой перевод к вспомогательной функции CLR, которая фактически создает объект. Вы можете посмотреть на эту вспомогательную функцию с исходным кодом SSCLI20, clr\src\vm\jithelpers.cpp. Слишком большой, чтобы публиковать здесь, но он сильно оптимизирован, чтобы сделать этот вид кода максимально быстрым, имея прямой доступ к внутренним объектам типов, доступным для кода CLR.

Доступны два из этих помощников: JIT_NewArr1 () создает одномерные (векторные) массивы, а JIT_NewMDArr () создает многомерные массивы. Сравните с двумя перегрузками, доступными для Type.MakeArrayType ().

9 голосов
/ 20 декабря 2010

И почему они (команда c #) сделали этот дизайн (я имею в виду, почему это не что-то вроде массива ...

Обобщения идеально подходят для определения контейнера, поскольку они ограничивают тип элемента, поэтому вы не можете вставить тип A и попытаться получить тип B.

Но генерики не были добавлены до CLR2 / C # 2. Таким образом, массивы должны были обеспечивать безопасность типов по-своему.

Несмотря на это, это не так уж отличается от дженериков. Вы заметите, что нет специального класса для int[]. Но и не будет для Array<int>. В дженериках будет только дженерик-класс Array<T>, и CLR «волшебным образом» создает специализированные версии для аргументов различных типов, которые вы используете. Так что было бы не менее "волшебно", если бы использовались дженерики.

Несмотря на это, в CLR тип любого объекта определяется (он существует как значение, которым вы можете манипулировать), типа Type и может быть получен с помощью typeof. Таким образом, хотя нет никакого объявления кода какого-либо типа массива (и зачем вам его нужно видеть?), Существует объект Type, к которому можно обратиться.

Кстати, в том, как массивы ограничивают типы элементов, был недостаток конструкции. Вы можете объявить массив:

int[] ints = ...

Затем вы можете сохранить его в более свободной переменной:

object[] objs = ints;

Но это означает, что вы можете вставить строку (по крайней мере, так будет во время компиляции):

objs[3] = "Oh dear";

Во время выполнения выдает исключение. Идея статической проверки типов заключается в том, чтобы отлавливать подобные вещи во время компиляции, а не во время выполнения. Обобщения не имели бы этой проблемы, потому что они не обеспечивают совместимость присваивания экземплярам обобщенного класса на основе совместимости их параметров типа. (Начиная с C # 4 / CLR4 они получили возможность делать это там, где это имеет смысл, но это не имеет смысла для изменяемого массива.)

7 голосов
/ 20 декабря 2010

Посмотрите на класс Array.

При объявлении массива с использованием синтаксиса [] компилятор за кулисами будет использовать этот класс для вас.

Для C # [] становится типом, который наследуется от System.Array.

Из спецификации C # 4.0:

§12.1.1 Тип System.Array

Тип System.Array является абстрактным базовым типом всех типов массивов. Неявное ссылочное преобразование (§6.1.6) существует из любого типа массива в System.Array, а явное ссылочное преобразование (§6.2.4) существует из System.Array в любой тип массива. Обратите внимание, что System.Array сам по себе не является типом массива. Скорее, это тип класса, из которого получены все типы массивов.

2 голосов
/ 20 декабря 2010

Я бы порекомендовал получить спецификацию ECMA 335 и поиск массивов, если вы хотите знать детали низкого уровня: http://www.ecma -international.org / публикации / стандарты / Ecma-335.htm

2 голосов
/ 20 декабря 2010

Есть такой класс. Вы не можете наследовать его, но когда вы пишете «int []», компилятор создает тип, который наследует System.Array. Так что если вы объявляете переменную:

int[] x;

Эта переменная будет иметь тип, который наследует System.Array, и, следовательно, имеет все свои методы и свойства.

Это также похоже на делегатов. Когда вы определяете делегата:

delegate void Foo(int x);
delegate int Bar(double x);

Тогда тип Foo на самом деле является классом, который наследует System.MulticastDelegate, а Bar является классом, который наследует System.Delegate.

1 голос
/ 11 марта 2015

Я начал копаться в спецификации ECMA 335 , так что я решил поделиться тем, что прочитал.

Точные типы массивов создаются VES автоматически, когда они требуются. Следовательно операции над типом массива определяются CTS. Как правило, это: выделение массива на основе размера и информации о нижней границе индексация массива для чтения и записи значения, вычисляют адрес элемента массива (управляемый указатель) и запрашивают ранг, границы и общее количество значений, хранящихся в массиве.

VES создает один тип массива для каждого различимый тип массива.

Векторы являются подтипами System.Array, абстрактного класса, предварительно определенного CLI. Это обеспечивает несколько методы, которые могут быть применены ко всем векторам. Смотрите Раздел IV.

Хотя векторы (§II.14.1) имеют прямую поддержку через инструкции CIL, все остальные массивы поддерживаются VES путем создания подтипов абстрактного класса System.Array (см. Раздел IV)

Хотя векторы (§II.14.1) напрямую поддерживаются с помощью инструкций CIL, все остальные массивы поддерживаются VES путем создания подтипов абстрактного класса System.Array (см. Раздел IV)

Класс, который VES создает для массивов, содержит несколько методов, реализация которых предоставляется по ВЭС:

Далее довольно подробно говорится, что предоставленные методы:

  1. Два конструктора
  2. Получить
  3. Установить
  4. Адрес (возвращает управляемый указатель)

VES означает Virtual Execution System , а CLR является его реализацией .

В спецификации также подробно описано, как хранить данные массива (непрерывно в порядке основной строки), какая индексация разрешена в массивах (только на основе 0), когда создается вектор (одномерные массивы на основе 0 ) в отличие от другого типа массива, когда используется инструкция CIL newarr вместо newobj (создание одномерного массива на основе 0).

В основном все, что компилятор должен делать для построения таблиц поиска методов и т. Д. Для обычного типа, это относится и к массивам, но они просто запрограммировали более универсальное и немного специальное поведение в компиляторе / JIT.

Почему они это сделали? Вероятно потому, что массивы являются особыми, широко используются и могут храниться оптимизированным способом. Однако команда C # не обязательно приняла это решение. Это больше похоже на .NET, двоюродный брат Mono и Portable.NET, которые все являются CIL.

0 голосов
/ 20 декабря 2010

[] - это синтаксис (синтетический сахар) для определения массивов в c #.Возможно, CreateInstance будет заменен во время выполнения

 Array a = Array.CreateInstance(typeof(int), 5); 

совпадает с

int[] a = new int[5];

Источник для CreateInstance (взят из отражателя)

public static unsafe Array CreateInstance(Type elementType, int length)
{
    if (elementType == null)
    {
        throw new ArgumentNullException("elementType");
    }
    RuntimeType underlyingSystemType = elementType.UnderlyingSystemType as RuntimeType;
    if (underlyingSystemType == null)
    {
        throw new ArgumentException(Environment.GetResourceString("Arg_MustBeType"), "elementType");
    }
    if (length < 0)
    {
        throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    }
    return InternalCreate((void*) underlyingSystemType.TypeHandle.Value, 1, &length, null);
}
0 голосов
/ 20 декабря 2010

Массивы специально для CLR.Они назначаются инструкцией 'newarr', а доступ к элементам осуществляется с помощью инструкций 'ldelem *' и 'stelem *', а не с помощью методов System.Array;

см. http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.newarr.aspx

Youможете проверить вывод ildasm, чтобы увидеть, как представлены массивы.

Итак, чтобы ответить на ваш вопрос - не создается никакого нового объявления типа для какого-либо конкретного массива.

...