C#.Почему массивы-типы ссылок?

167
21 сентября 2021, 04:10

Узнал что массивы в C#-это типы ссылок и они хранятся в куче,которая используется для динамичного выделения памяти.Но вот вопрос:для чего массиву динамичное выдиление памяти?.Вот например мы объявляем массив int[] newArray = new [4]; Мы заранее знаем что массив будет принимать 4 елемнта типа Int32 и мы заранее знаем сколько памяти ему потребуется.Так почему массивы-типы ссылок?

Answer 1

"Сколько памяти потребуется" важно не столько при выделении памяти, сколько при хранении массива в других объектах.

Например, у вас есть класс вида

class A
{
   public int fieldA;
   public int fieldB; 
}

И есть работающий с ним код вида

var a = new A();
a.fieldB = 42;

Перед реальным выполнением этот код надо превратить в код для конкретной платформы. JIT компилятор, который этим занимается, заранее знает, сколько объект типа A занимает в памяти, и по какому смещению в нем лежит поле fieldB:

[0 header][+24 fieldA][+28 fieldB] // 24 - условный размер заголовка

и он генеренирует что-то вроде

mov [a + 28], 42 // положить по адресу a + смещение в нем поля fieldB значение 42

И все это работает быстро. Теперь добавим поле-массив (в котором может лежать массив, а может и не лежать):

class A
{
   public int fieldA;
   public int[] fieldArray;
   public int fieldB; 
}

Получаем объект вида

[0 header][+24 fieldA][+28 fieldArray][+32 fieldB] // размер fieldArray предсказуем, т.к. массив - ссылочный тип, и в этом поле лежит адрес массива.

и код при выполнении

mov [a + 32], 42 // положить по адресу a + смещение в нем поля fieldB значение 42

Ок, теперь представим что int[] - это value type.

[0 header][+24 fieldA][+28 fieldArray][+??? fieldB] // размер fieldArray заранее неизвестен 

Как теперь в этой ситуации записать значение в fieldB? Реально проверять размер массива перед каждым обращением в соседнее поле? Перекомпилировать метод из IL в C# заново под каждый объект? Дорого, сложно и неэффективно. Разрешить массивы (и, соответственно, строчки) только заранее известной длины? Ну тогда на языке вообще будет тяжело что-то реальное написать :)

А вот если убрать необходимость как-то хранить массивы в других объектах - то все ок, можно делать из них value type. Именно так сделано для структуры Span<T>, которая, по сути, и есть value type array.

Вот, выделяет 100 байт чистейшего стека, без всяких там ссылок:

Span<byte> stackSpan = stackalloc byte[100];
Answer 2

На вопрос "Почему?" краткий ответ таков: потому что на данный момент так сделано.

Можно ли сделать иначе: если массив умещается на стеке - размещать его там, не умещается - размещать в куче? Да, можно. В JVM делается именно так (во всяком случае, в некоторых реализациях HotSpot). Почему не сделано в CLR? Не всё сразу. Разработчики .NET предпочли реализовывать другие фичи (значимые типы, дженерики, указатели на неуправляемую память и пр.)

Однако, работы по определению, где выгоднее разместить объекты, ведутся. Object Stack Allocation. Надеюсь, эти теоретические работы перейдут в практическую плоскость.

Answer 3

Потому что можно сделать так:

int[] i;
i = new int[15];
//тут какое либо использование
i = new int[25];
//ещё что либо
n = int.Parse(Console.ReadLine()/*Любой другой способ получения информации от пользователя*/)
 i = new int[n];
//ещё что либо

А если быть более честым, то c# полностью объектно-ориентированный, и всё что в нём есть - объект, а объект - это составная сущность которая занимает (зачастую) неопределённое количество памяти.

Мы заранее знаем что массив будет принимать 4 елемнта типа Int32 и мы заранее знаем сколько памяти ему потребуется.

Да и тут я бы с вами поспорил. А как же вычисление значений не на этапе компиляции, а в рантайме?(во время выполнения)

Простой пример, который я часто использую: мне бывает лень объявлять масивчики и я пользуюсь таким методом(читай: отрывок паттерна Фабрика. хоть вообще не красивый, не правильный и нельзя так делать, но блин удобно)

GetArr<T>(int lenght)=> new T[lenght];

Это универсальный метод для создания Массива любого типа.

А ещё, небольшая разница между тем же самым Int32. Это не класс а структура, а структуры являются TypeValue - значениями, и соответственно хранятся в стеке, а Array является производным от Object, и соответственно располагается в Куче

READ ALSO
Получить текст с экрана

Получить текст с экрана

Я переписываю код WPF в UWPЗадача состоит в том, чтобы сделать скриншот экрана, вырезать его и скормить его библиотеке, которая найдет текст...

78
Как перебрать большой массив на 60000 строк?

Как перебрать большой массив на 60000 строк?

на пк если запустить такой скрипт то комп очень долго думает возможно например етот код залить на VPS сервак чтобы он быстро ответ давал, или...

75
Как получить значение тега из SimpleXMLElement?

Как получить значение тега из SimpleXMLElement?

Как получить значение тега <category> и записать его в масив?

100
Как подключать стили в php?

Как подключать стили в php?

Я новичок в phpСоздал папку в которой 2 файла - index

74