В чем разница static переменной в классе и для чего она используется
Что проверяют
Понимаете ли вы, что статическое поле живёт на уровне типа, а не объекта, и чем это оборачивается в многопоточном коде. Следом обычно спрашивают, когда выполняется статический конструктор и почему статическое состояние мешает тестам.
Как отвечать
Начните с разницы. Поле экземпляра у каждого объекта своё, статическое поле существует в одной копии на тип, и обращаются к нему через имя класса: Counter.Total, а не через объект. Дальше конкретика:
- инициализация: статический конструктор выполняется один раз, до первого обращения к статическому члену или до создания экземпляра, и CLR гарантирует его потокобезопасность. Если явного статического конструктора нет, тип помечается
beforefieldinit, и среда вправе инициализировать поля раньше; - generic-типы: у каждого закрытого типа своя копия,
Cache<int>.ItemsиCache<string>.Itemsэто разные поля; constиstatic readonly:constподставляется в вызывающую сборку при компиляции,static readonlyвычисляется в рантайме. Поменялиconstв библиотеке и не пересобрали зависимые сборки, получили старое значение;[ThreadStatic]иAsyncLocal<T>: когда нужна копия на поток или на асинхронный поток выполнения;- применение: кэши, счётчики, неизменяемые настройки,
static classдля утилит и методов расширения.
Про потоки: count++ не атомарна, для счётчика нужен Interlocked.Increment, для сложного состояния lock или ConcurrentDictionary. В ASP.NET Core статическое поле общее для всех запросов всех пользователей, поэтому изменяемое состояние лучше держать в Singleton-сервисе из DI, который подменяется в тестах.
Где спотыкаются
Говорят, что статика инициализируется «при запуске программы». На деле это происходит лениво, при первом обращении к типу, и порядок инициализации разных классов не гарантирован.
Забывают про утечки. Статическое поле это корень для сборщика мусора: подписка на статическое событие или растущий статический List удерживают объекты до конца жизни процесса.
Не знают, что исключение в статическом конструкторе превращается в TypeInitializationException, и тип остаётся непригодным до перезапуска приложения.