Перейти к содержимому
Теория · Классы и объекты · урок 8 из 11

Статические члены

В углу чертежа архитектор ведёт счёт: сколько домов по нему уже построено. Это число не относится ни к одному дому. У красного дома нет своего «построено два», и у синего нет. Оно одно на весь чертёж, и после каждой новой стройки его увеличивают на единицу.

До сих пор у каждого объекта были свои поля и свойства: свой цвет, свои этажи. Но иногда данные нужны одни на весь класс. Для этого есть слово static:

C#
var a = new House("red");
var b = new House("blue");
Console.WriteLine(House.Count); // 2

class House
{
    public static int Count { get; private set; }
    public string Color { get; }

    public House(string color)
    {
        Color = color;
        Count++;
    }
}

Давайте разберём программу:

  1. public static int Count { get; private set; } — статическое свойство: Count один на весь класс House, а не по одному в каждом доме.
  2. public string Color { get; } — обычное свойство: у каждого дома свой цвет.
  3. Каждый вызов конструктора выполняет Count++ и увеличивает тот же самый счётчик.
  4. House.Count читают через имя класса, а не через дом, и строка печатает 2.

Статический член — член, который принадлежит самому классу, а не его объектам. Обычные члены, как Color, есть у каждого объекта свои.

Через имя класса

Статический член существует, даже когда объектов нет совсем: до первого new выражение House.Count равно 0. Поэтому к нему обращаются через имя класса. Строка a.Count не скомпилируется: ошибка CS0176, компилятор просит написать имя типа.

Константа const тоже принадлежит типу, а не объекту. Поэтому вы пишете int.MaxValue: эта константа одна для всех чисел int.

Здесь чертёж и класс расходятся. Построенный дом не может ничего дописать на чертеже, а объект может менять статический член своего класса. Именно так конструктор каждого нового дома увеличивает Count.

Статические методы

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

C#
Console.WriteLine(House.Height(5)); // 15

class House
{
    public static int Height(int floors) => floors * 3;
}

Чтобы узнать высоту пятиэтажного дома по три метра на этаж, дом строить не нужно: хватает числа этажей. Поэтому Height — статический метод.

Статическими методами вы пользуетесь с первой программы курса:

  • Console.WriteLine — объект Console никто не создаёт;
  • int.Parse и int.TryParse — их вызывают через имя типа int;
  • Array.Sort — его вызывают через имя класса Array, а массив передают аргументом.

И static void Main() из темы о первой программе статический по той же причине: среда выполнения вызывает Main, не создавая объект класса Program.

Статический или обычный

Об этом часто спрашивают на собеседовании: «Чем статический член отличается от обычного?» Ответ из трёх частей:

  • статический член один на весь класс, а обычный есть у каждого объекта свой;
  • к статическому обращаются через имя класса, к обычному — через объект;
  • статический метод не может обратиться к обычным членам напрямую: у него нет this, нет своего объекта.

Член делают статическим, когда ему не нужны данные конкретного объекта: общий счётчик, расчёт только по параметрам метода.

Частая ошибка

Статический метод читает обычное свойство:

C#
var a = new House("red");
Console.WriteLine(House.Describe());

class House
{
    public string Color { get; }

    public House(string color) => Color = color;

    public static string Describe() => "Color: " + Color; // ✗
}
TEXT
error CS0120: An object reference is required for the non-static field, method, or property 'House.Color'

Программа не компилируется. Статический метод вызывают через класс, без дома, поэтому непонятно, чей цвет он должен прочитать. Сообщение так и говорит: чтобы обратиться к обычному члену House.Color, нужна ссылка на объект. Уберите static и вызывайте метод через дом: a.Describe() вместо House.Describe(). Тогда он прочитает цвет того дома, для которого его вызвали.

Главное

  • Статический член, объявленный со словом static, один на весь класс; обычные члены есть у каждого объекта свои.
  • К статическому члену обращаются через имя класса: House.Count, int.Parse; через объект — не компилируется.
  • Статический метод работает без объекта, поэтому к обычным полям и свойствам напрямую обратиться не может.
  • Console.WriteLine, int.Parse и static void Main() — статические методы.