Menu

static в C#: статические классы, методы, поля и конструкторы

Что означает static в C#: члены, принадлежащие типу, а не объекту, общее состояние всех экземпляров, статические методы и классы, статические конструкторы, const и static readonly, using static и ошибка CS0120.

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

static помечает член, который принадлежит типу, а не объекту. Поле экземпляра существует по одному на объект; статическое поле существует в единственном экземпляре, и всё. Метод экземпляра выполняется на объекте (order.Ship()); статический метод выполняется на типе (Math.Round(x)), и объект ему вообще не нужен.

Статические поля: одна копия на всех

Разницу нагляднее всего показывает счётчик. У каждого Order свой Id, но количество созданных на данный момент заказов должно храниться где-то в общем месте.

Вывод:

Ana #1, Ben #2, Chen #3
Orders created: 3

К статическим членам обращаются через имя типа: Order.Created, а не a.Created. C# не позволяет обращаться к статическому члену через экземпляр (ошибка CS0176), поэтому в месте вызова сразу видно, что значение общее.

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

У статического метода нет this. Он работает только со своими параметрами и другими статическими членами. Это естественная форма для вспомогательных функций, которые что-то вычисляют из своих входных данных:

Вывод:

61.38 EUR
7

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

Ошибка CS0120: статический код использует члены экземпляра

Поскольку Main статический, новички часто получают эту ошибку, как только добавляют второй метод:

class Program
{
    int visits;
    void Greet() { Console.WriteLine("Hello"); }

    static void Main()
    {
        Greet();    // error CS0120: An object reference is required for the non-static field, method, or property 'Program.Greet()'
        visits++;   // error CS0120: An object reference is required for the non-static field, method, or property 'Program.visits'
    }
}

У статического метода нет объекта, поэтому нет и visits, которое он мог бы увеличить. Есть два исправления, и правильное зависит от того, чем является член:

  • Член действительно хранит данные конкретного объекта: создайте объект и используйте его. var app = new Program(); app.Greet();
  • Члену не нужно состояние объекта: пометьте его static. static void Greet() { ... }

Пометка всего подряд как static, чтобы избавиться от ошибки, работает в крошечной программе и превращается в глобальное состояние в настоящей. Решайте, спрашивая себя, принадлежат ли данные одному объекту или всей программе.

Обратное направление разрешено всегда: методы экземпляра могут свободно вызывать статические члены.

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

Класс, объявленный static, может содержать только статические члены, и его экземпляр никогда нельзя создать. Это именованная группа функций и констант.

Вывод:

77
100
True

Компилятор следит за этим правилом: член экземпляра внутри статического класса это ошибка CS0708, а new для него ошибка CS0712. Math, Console, File, Path и Enumerable это статические классы. Методы расширения тоже должны находиться в статическом классе.

Статические конструкторы и static readonly

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

Вывод:

Before first use
ShippingRates initialized
5.50
3
2026-01-01

readonly означает, что поле можно присвоить только в его инициализаторе или в статическом конструкторе. После этого его нельзя переприсвоить, хотя объект, на который оно указывает, всё ещё может быть изменяемым, поэтому словарь открыт как IReadOnlyDictionary.

const или static readonly

Оба дают именованное значение, общее для всей программы, и различаются тем, когда значение фиксируется:

conststatic readonly
Значение фиксируетсяпри компиляцииво время выполнения, один раз
Допустимые типычисла, char, bool, string, перечисления, nullлюбой тип
Неявно статическоеда (нельзя написать static const)static указывается явно
Копируется в вызывающие сборкиданет, читается из поля
Можно использовать в case и аргументах атрибутовданет

Строка о копировании важна для библиотек: если сборка A объявляет public const int MaxItems = 50;, а сборка B его использует, B содержит литерал 50. Изменение A на 100 ничего не меняет для B, пока B не перекомпилируют. Для значений, которые могут меняться между версиями, предпочитайте static readonly.

using static

Директива using static (C# 6) импортирует статические члены типа, чтобы вызывать их без имени типа:

Вывод:

Area: 28.27
40

Это хорошо читается в коде с большим количеством математики. В остальных местах используйте экономно: одинокий WriteLine или Parse скрывает, из какого типа пришёл метод.

Статическое состояние и потоки

Статическое поле общее для всех потоков процесса. Одновременное увеличение из нескольких потоков теряет обновления, потому что created++ это чтение, сложение и запись, и два потока могут чередовать эти шаги. Для счётчиков используйте Interlocked.Increment(ref created), для всего крупнее lock. Обычно это кусается в веб-приложениях: каждый запрос выполняется в каком-то потоке, поэтому статическое поле с «текущим пользователем» общее для всех пользователей.

Ещё одна тонкость: в обобщённом классе каждый сконструированный тип получает собственные статические поля. Cache<int>.Count и Cache<string>.Count это два разных поля.

Когда использовать static

Хорошие применения:

  • Чистые вспомогательные функции, вычисляющие результат из аргументов: форматирование, преобразования, проверки.
  • Константы и справочные данные только для чтения, общие для программы.
  • Фабричные методы, например TimeSpan.FromMinutes(5) или ваш собственный Temperature.FromCelsius(21).
  • Методы расширения.

Плохие применения:

  • Изменяемое глобальное состояние (текущий пользователь, корзина покупок, меняющиеся настройки). Оно связывает весь код, который его касается, и делает тесты зависимыми от того, что оставили другие тесты.
  • Всё, что захочется подменить в тестах, например часы, базу данных или отправку писем. Статический вызов нельзя заменить подделкой, а экземпляр, переданный в конструктор, можно.

Часто задаваемые вопросы

Что означает static в C#?

Член static принадлежит самому типу, а не какому-либо объекту. Статическое поле существует в одном экземпляре, сколько бы объектов ни было, а статический метод вызывается через имя типа (Math.Max(3, 7)) без создания экземпляра. У статического метода нет this, поэтому напрямую использовать поля экземпляра он не может.

Что такое статический класс в C#?

Класс, объявленный как static, может содержать только статические члены, и его экземпляр никогда нельзя создать (new для него это ошибка CS0712). Он группирует связанные функции, которым не нужно состояние объекта, как Math, Console или File. Методы расширения должны объявляться в статическом классе.

Как исправить ошибку «An object reference is required for the non-static field, method, or property»?

Это ошибка CS0120: статический метод, часто Main, использует член экземпляра. Либо создайте объект и вызовите член на нём (var app = new Program(); app.Run();), либо пометьте член как static, если ему не нужны данные конкретного объекта.

Чем const отличается от static readonly в C#?

const это константа времени компиляции (числа, строки, bool, перечисления), значение которой копируется в каждую использующую её сборку. Поле static readonly присваивается один раз во время выполнения, в инициализаторе или статическом конструкторе, и может иметь любой тип, например DateTime или список. Используйте const для значений, которые никогда не изменятся, и static readonly для всего остального.

Когда использовать static в C#?

Используйте static для кода, который работает только со своими параметрами (чистые вспомогательные функции вроде форматирования цены), для констант и кэшей, которые действительно глобальны для программы, и для фабричных методов. Избегайте static для всего, что хранит состояние пользователя или запроса или что вы захотите подменить в тестах: общее изменяемое состояние трудно тестировать, и без блокировок оно небезопасно в многопоточном коде.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ