Что происходит при создании нового объекта через оператор new в C#?

Ответ

Оператор new инициирует процесс выделения памяти и инициализации экземпляра типа (класса или структуры). Для ссылочных типов (классов) это происходит так:

  1. Выделение памяти: Среда выполнения CLR выделяет непрерывный блок памяти в управляемой куче (managed heap) под все поля объекта. Если в куче недостаточно свободного места, запускается сборка мусора (GC).
  2. Инициализация памяти: Все поля объекта устанавливаются в значения по умолчанию (0, null, false).
  3. Вызов конструктора: Выполняется цепочка конструкторов (от базового класса к производному), которая инициализирует объект заданными значениями.
  4. Возврат ссылки: Оператор new возвращает ссылку (адрес в памяти) на созданный объект. Эта ссылка хранится в переменной.

Пример кода:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Person(string name, int age)
    {
        Name = name; // Инициализация в конструкторе
        Age = age;
    }
}

// Процесс создания:
Person person = new Person("Alice", 30);
// 1. Выделена память в куче для полей Name (ссылка) и Age (int).
// 2. Поля установлены в null и 0.
// 3. Вызван конструктор Person("Alice", 30).
// 4. В переменную 'person' помещена ссылка на этот блок памяти.

Важные отличия для значимых типов (struct):

  • Для локальных переменных struct память выделяется в стеке вызовов.
  • Когда struct является полем класса, она хранится в куче внутри памяти своего содержащего объекта.
  • Процесс инициализации (поля по умолчанию -> конструктор) аналогичен, но без выделения памяти в куче и без возврата ссылки (работа идет с самим значением).

Ответ 18+ 🔞

А, ну это про new! Давай разберём, как эта штука работает, а то некоторые думают, что это просто волшебная палочка, которая объекты из воздуха материализует. Ёпта, нет же, там под капотом целый цирк с конями.

Вот смотри, когда ты пишешь для класса, типа MyClass obj = new MyClass();, начинается такая движуха:

  1. Выделение памяти в куче. CLR, эта наша всеобщая мать-менеджер, ищет в управляемой куче свободное местечко. Если места нет — охуеть! — запускается сборщик мусора (GC), который начинает выносить весь хлам, который уже никому не нужен. Освободил место — можно твой объект пихать.
  2. Зануление полей. Пока конструктор ещё даже не нюхал, все поля твоего будущего объекта обнуляются. Ссылки в null, числа в 0, булевы в false. Чистенький, стерильный кусок памяти, блядь.
  3. Запуск конструктора. Вот тут уже начинается инициализация по-твоему. Выполняется цепочка конструкторов, от базового класса к твоему, чтобы всё настроить как надо.
  4. Возврат ссылки. И вот, наконец, оператор new тебе в переменную obj подкидывает не сам объект, а его адресок в памяти — ссылку. Объект-то в куче сидит, а у тебя в руках пульт управления на него.

Пример, чтобы совсем доходило:

public class Person
{
    public string Name { get; set; } // Ссылочный тип, изначально null
    public int Age { get; set; }     // Значимый тип, изначально 0

    public Person(string name, int age)
    {
        Name = name; // Теперь ссылка указывает на строку "Alice"
        Age = age;   // А тут уже 30
    }
}

// Сам момент истины:
Person person = new Person("Alice", 30);
// По шагам:
// 1. Щупаем кучу, ищем место под поля Name и Age.
// 2. Обнуляем: Name = null, Age = 0.
// 3. Конструктор: Name = "Alice", Age = 30.
// 4. В переменную person кладём не сам объект, а бумажку с его адресом в куче.

А теперь про структуры (struct), тут прикол!

Со структурами история немного другая, ебать колотить. Если struct объявлена как локальная переменная в методе, память под неё выделяется прямо в стеке вызовов, быстро и без лишних церемоний. Никакой кучи, никакого мусорщика.

Но если эта структура — поле внутри класса, то она тупо лежит в куче, как часть того объекта, который её содержит. Просто кусок данных, без своей отдельной ссылки.

Процесс инициалиции похож: сначала поля в значения по умолчанию, потом конструктор. Но ключевая разница — new для структуры часто возвращает само значение, а не ссылку на него. Хотя, честно говоря, компилятор иногда может эту операцию и вовсе оптимизировать, если видит, что можно без неё обойтись. Вот такая магия, только без волшебства, а с чёткой работой памяти.