Подтвердить что ты не робот

Какая магия массивов в С#

int[] a = new int[5];
string[] b = new string[1];

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

4b9b3361

Ответ 1

Попытка объяснить это в системе типа .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)

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

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

Ответ 2

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

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

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

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

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

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

int[] ints = ...

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

object[] objs = ints;

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

objs[3] = "Oh dear";

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

Ответ 3

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

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

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

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

§12.1.1 Тип System.Array

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

Ответ 4

Существует такой класс. Вы не можете наследовать его, но когда вы пишете "int []", компилятор создает тип, который наследует System.Array. Поэтому, если вы объявляете переменную:

int[] x;

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

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

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

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

Ответ 6

Я перешел через спецификацию 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 для массивов, содержит несколько методов, реализация которых предоставляется VES:

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

  • Два конструктора
  • Получить
  • Set
  • Адрес (возвращает управляемый указатель)

VES означает Virtual Execution System, а CLR - это реализация.

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

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

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

Ответ 7

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

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

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

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

Ответ 8

[] - синтаксис (синтаксический сахар) для определения массивов в С#. Возможно, 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);
}