Как стать автором
Обновить

Порядок инициализации полей, статики и всего остального в C#

Уровень сложностиПростой
Время на прочтение4 мин
Количество просмотров5.1K

Всем привет! Многие сталкиваются с трудностями на собеседовании на вопросе по типу "Расскажите о порядке иницализации в C#". Либо банально когда видят квиз, стараются вспомнить, а что там должно инициализироваться? Сегодня многие вспомнят, кто-то узнает о порядке инициализации. Затронем не только классы, а также структуры, а точнее - ключевое слово default для них.

Сделаем следующие классы и посмотрим, что будет при создании объекта B:

class A
{
    public int a =  Foo("pole a");
    public static int a1 = Foo($"вывод константы {con}\npole static a1");
    public const int con = 2;

    public A()
    {
        Console.WriteLine("const A");
    }

    static A()
    {
        Console.WriteLine("static const A");
    }

    public static int Foo(string c)
    {
        Console.WriteLine(c);
        return 1;
    }
}

class B:A
{
    public int b = Foo("pole b");
    public static int b1 = Foo("pole const b1");
    public B()
    {
        Console.WriteLine("const B");
    }

    static B()
    {
        Console.WriteLine("static const B");
    }
}

Единственное что нужно запомнить - каждый шаг работает с фукнцией Foo(она служит оповещением о пройденном этапе). У нас есть константа (c неё и начнём). Она определяется на этапе КОМПИЛЯЦИИ. То есть её значение мы узнаем первыми.

Но что дальше? А дальше - нужно запомнить пару вещей (которые все давно помнят, но как это работает под капотом - не многие задумывались).

Сначала в бой идут статические конструкторы. Знает уже каждый. Но что же с полями? А всё просто - запоминаем простыми словами: "В констуркторе сначала идет инициализация полей пользователем, а дальше сама работа констурктора". То есть в Low-level коде всё выглядит попроще. Берем класс A:

internal class A
{
  public int a;
  public static int a1;
  public const int con = 2;

  public A()
  {
    this.a = A.Foo("pole a");
    base..ctor();
    Console.WriteLine("const A");
  }

  static A()
  {
    DefaultInterpolatedStringHandler interpolatedStringHandler = new DefaultInterpolatedStringHandler(31, 1);
    interpolatedStringHandler.AppendLiteral("вывод константы ");
    interpolatedStringHandler.AppendFormatted<int>(2);
    interpolatedStringHandler.AppendLiteral("\npole static a1");
    A.a1 = A.Foo(interpolatedStringHandler.ToStringAndClear());
    Console.WriteLine("static const A");
  }

  [NullableContext(1)]
  public static int Foo(string c)
  {
    Console.WriteLine(c);
    return 1;
  }
}

Наши поля, которым мы задали значения - пусты (кроме константы)! Они инициализированы по умолчанию. А присвоение им значений происходит в соответствующих конструкторах (обычное поле - к обычному конструктору, статик - к статику). Теперь выставляем по полочкам:
Идёт сначала константа, далее статик конструктор (в нём сначала инициализация поля, далее работа конструктора), а после - обычный конструктор (логика та же, что и в статике).

Но у нас 2 класса, создаём мы дочерний класс, что нам выведется в итоге?

Давайте посмотрим на класс B в Low-level code:

internal class B : A
{
  public int b;
  public static int b1;

  public B()
  {
    this.b = A.Foo("pole b");
    base..ctor();
    Console.WriteLine("const B");
  }

  static B()
  {
    B.b1 = A.Foo("pole const b1");
    Console.WriteLine("static const B");
  }
}

Всё знакомо, видим (и помним!), что статик конструкторы не наследуются. А теперь смотрим на обычный конструктор - видим, что также сначала идет инициализация обычного поля b, а потом - вызывается конструктор родителя (если много наследований, всё будет также, получается "конструктор в конструкторе" для каждого класса). В конструкторе родителя (мы его смотрели выше) будет:

  public A()
  {
    this.a = A.Foo("pole a");
    base..ctor();
    Console.WriteLine("const A");
  }

То есть Foo для обычного поля, потом работа конструктора. Значит, после поля b мы приступим не к конструктору B, а к полю a.

Итоговый вывод:

вывод константы 2
pole static a1
static const A
pole const b1
static const B
pole b
pole a
const A
const B

Получается: идет работа статик конструкторов классов по очереди от родительского класса (тк мы в классе B можем брать стат поля из класса A, всё логично), далее идёт инициализация поля b, потом работа конструктора A (поле a + сам конструктор) и только потом работа конструктора B.

Выглядит громоздко, но запоминаем 2 вещи - сначала идут все статик конструкторы, потом обычные констуркторы + что такое конструктор в Low-level C# (работа с полем + наш конструктор).

А теперь к структуре - всё то же самое. Единственное слово default многих вводит в ступор. Вспоминаем, оно присваивает переменной дефолт значение. Для int - 0, для типов допускащих налл - null. Но что же со структурами?

Проверяем:

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = default;
            Console.WriteLine(A.a1);
            Console.WriteLine(a.a);
        }

        private A a1;
    }
        
}

struct A
{
    public int a =  Foo("pole a");
    public static int a1 = Foo($"вывод константы {con}\npole static a1");
    public const int con = 2;

    public A()
    {
        Console.WriteLine("const A");
    }

    static A()
    {
        Console.WriteLine("static const A");
    }

    public static int Foo(string c)
    {
        Console.WriteLine(c);
        return 1;
    }
}

Создаём по той же идее СТРУКТУРУ A и тестим, заметьте, поле без вызова конструктора и присвоение default - одно и то же по записи, это всё дефолт.

Запускаем

вывод константы 2
pole static a1
static const A
1
0

Обычный конструктор - не запускается, как и статик (его работа начинается, только если будем взаимодействовать с статик полем, поэтому вывод у статик поля - 1). Конструктор не изменил значение НЕстатик поля (вывод 0 в конце) и сам не сработал. Дефолт ставит значения всех полей структуры по умолчанию, что может быть неожиданно (особенно если поле класса - структра, и мы забудем у него явно активировать конструктор). На этом и закончим, запомните, что такое конструктор и будет вам счастье!

Теги:
Хабы:
Всего голосов 3: ↑3 и ↓0+3
Комментарии2

Публикации

Работа

Ближайшие события