Статические конструкторы (Руководство по программированию в C#)
Статический конструктор используется для инициализации любых статических данных или для выполнения определенного действия, которое требуется выполнить только один раз. Он вызывается автоматически перед созданием первого экземпляра или ссылкой на какие-либо статические члены. Статический конструктор будет вызываться по крайней мере один раз.
class SimpleClass
{
// Static variable that must be initialized at run time.
static readonly long baseline;
// Static constructor is called at most one time, before any
// instance constructor is invoked or member is accessed.
static SimpleClass()
{
baseline = DateTime.Now.Ticks;
}
}
Существует несколько действий, которые являются частью статической инициализации. Эти действия выполняются в следующем порядке:
- Статические поля имеют значение 0. Обычно это делается средой выполнения.
- Запуск инициализаторов статических полей. Инициализаторы статических полей в большинстве производных типов.
- Запуск инициализаторов статических полей базового типа. Инициализаторы статических полей, начиная с прямой базы с каждым базовым типом System.Object.
- Запуск базовых статических конструкторов. Все статические конструкторы, начиная с Object.Object каждого базового класса, до прямого базового класса.
- Выполняется статический конструктор. Статический конструктор для типа выполняется.
Инициализатор модуля может быть альтернативой статическому конструктору. Дополнительные сведения см. в спецификации для инициализаторов модулей.
Замечания
Статические конструкторы обладают следующими свойствами.
- Статический конструктор не принимает модификаторы доступа и не имеет параметров.
- Класс или структура могут иметь только один статический конструктор.
- Статические конструкторы не могут быть унаследованы или перегружены.
- Статический конструктор нельзя вызывать напрямую. Он предназначен только для вызова из общеязыковой среды выполнения (CLR). Он запускается автоматически.
- Пользователь не управляет временем, в течение которого статический конструктор выполняется в программе.
- Статический конструктор вызывается автоматически. Он инициализирует класс перед созданием первого экземпляра или ссылок на какие-либо статические элементы, объявленные в этом классе (не его базовых классах). Статический конструктор выполняется раньше, чем конструктор экземпляра. Если в классе со статическим конструктором присутствуют инициализаторы переменных для статических полей, они выполняются в том порядке, в котором они появляются в объявлении класса. Эти инициализаторы выполняются непосредственно перед выполнением статического конструктора.
- Если вы не предоставили статический конструктор для инициализации статических полей, все статические поля инициализируются значениями по умолчанию, которые указаны в разделе Значения по умолчанию типов C#.
- Если статический конструктор создает исключение, среда выполнения не вызывает его во второй раз, и тогда тип остается неинициализированным на все время существования домена приложения. Чаще всего исключение TypeInitializationException вызывается, когда статический конструктор не может создать экземпляр типа или в статическом конструкторе создается необработанное исключение. Для устранения неполадок со статическими конструкторами, которые не определены явным образом в исходном коде, может потребоваться проверка кода на промежуточном языке (IL).
- Наличие статического конструктора не позволяет добавлять атрибут типа BeforeFieldInit. Это ограничивает возможности оптимизации во время выполнения.
- Поле, объявленное как
static readonly
, может быть присвоено только при его объявлении или в статическом конструкторе. Если явный статический конструктор не требуется, инициализируйте статические поля в объявлении, а не через статический конструктор, чтобы оптимизировать среду выполнения. - Среда выполнения вызывает статический конструктор не более одного раза для каждого домена приложения. Этот вызов выполняется в заблокированном регионе на основе определенного типа класса. В тексте статического конструктора не нужны дополнительные механизмы блокировки. Чтобы избежать риска взаимоблокировки, не блокируйте текущий поток в статических конструкторах и инициализаторах. Например, не следует ожидать выполнения задач, потоков, обработчиков ожидания или событий, а также создавать блокировки или выполнять блокирующие параллельные операции, как, например, параллельные циклы,
Parallel.Invoke
и запросы Parallel LINQ.
Примечание.
Наличие явного статического конструктора следует документировать, несмотря на отсутствие прямого доступа к нему, поскольку это важно для устранения неполадок с исключениями при инициализации.
Использование
- Типичным использованием статических конструкторов является случай, когда класс использует файл журнала и конструктор применяется для добавления записей в этот файл.
- Статические конструкторы также полезны при создании классов-оболочек для неуправляемого кода, когда конструктор может вызвать метод
LoadLibrary
. - Кроме того, статические конструкторы очень удобны для проведения проверок времени выполнения для параметров типов, которые нельзя проверить во время компиляции с помощью ограничений параметров типов.
Пример
В этом примере класс Bus
имеет статический конструктор. При создании первого экземпляра класса Bus
(bus1
) для инициализации класса вызывается статический конструктор. В выходных данных этого примера можно увидеть, что статический конструктор выполняется только один раз, несмотря на то, что создается два экземпляра класса Bus
. Кроме того, этот конструктор вызывается до выполнения конструктора экземпляра.
public class Bus
{
// Static variable used by all Bus instances.
// Represents the time the first bus of the day starts its route.
protected static readonly DateTime globalStartTime;
// Property for the number of each bus.
protected int RouteNumber { get; set; }
// Static constructor to initialize the static variable.
// It is invoked before the first instance constructor is run.
static Bus()
{
globalStartTime = DateTime.Now;
// The following statement produces the first line of output,
// and the line occurs only once.
Console.WriteLine("Static constructor sets global start time to {0}",
globalStartTime.ToLongTimeString());
}
// Instance constructor.
public Bus(int routeNum)
{
RouteNumber = routeNum;
Console.WriteLine("Bus #{0} is created.", RouteNumber);
}
// Instance method.
public void Drive()
{
TimeSpan elapsedTime = DateTime.Now - globalStartTime;
// For demonstration purposes we treat milliseconds as minutes to simulate
// actual bus times. Do not do this in your actual bus schedule program!
Console.WriteLine("{0} is starting its route {1:N2} minutes after global start time {2}.",
this.RouteNumber,
elapsedTime.Milliseconds,
globalStartTime.ToShortTimeString());
}
}
class TestBus
{
static void Main()
{
// The creation of this instance activates the static constructor.
Bus bus1 = new Bus(71);
// Create a second bus.
Bus bus2 = new Bus(72);
// Send bus1 on its way.
bus1.Drive();
// Wait for bus2 to warm up.
System.Threading.Thread.Sleep(25);
// Send bus2 on its way.
bus2.Drive();
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Sample output:
Static constructor sets global start time to 3:57:08 PM.
Bus #71 is created.
Bus #72 is created.
71 is starting its route 6.00 minutes after global start time 3:57 PM.
72 is starting its route 31.00 minutes after global start time 3:57 PM.
*/
Спецификация языка C#
Дополнительные сведения см. в разделе Статические конструктор в спецификации языка C#.
См. также
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по