Когда вызываются статические конструкторы классов в c

Обновлено: 26.04.2024

Давайте рассмотрим одну из наиболее простых и популярных реализаций паттерна Синглтон (*), основанную на инициализаторе статического поля:

public sealed class Singleton
private static readonly Singleton instance = new Singleton();

public static Singleton Instance < get < return instance; >>
>

* This source code was highlighted with Source Code Highlighter .

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

Статический конструктор и инициализаторы полей

Итак, давайте рассмотрим следующий код:

public static string S = Echo( "Field initializer" );

public static string Echo( string s)
Console .WriteLine(s);
return s;
>
>

static void Main( string [] args)
Console .WriteLine( "Starting Main. " );
if (args.Length == 1)
Console .WriteLine(Singleton.S);
>
Console .ReadLine();
>
>

* This source code was highlighted with Source Code Highlighter .

Как видно, что статическое поле будет проинициализировано, хотя сам тип в приложении не используется. Практика показывает, что в большинстве случаев при отсутствии явного конструктора, JIT-компилятор вызывает инициализатор статических переменных непосредственно перед вызовом метода, в котором используется эта переменная. Если раскомментировать статический конструктор класса Singleton, то поведение будет именно таким, которое ожидает большинство разработчиков – инициализатор поля вызван не будет и при запуске приложения на экране будет только одна строка: Starting Main…”.

Статические конструкторы и взаимоблокировка

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

Воспроизвести подобную ситуацию в идеальных условиях весьма просто: для этого в статическом конструкторе достаточно создать новый поток и попытаться дождаться его выполнения:

class Program
static Program()
var thread = new Thread(o => < >);
thread.Start();
thread.Join();
>

static void Main()
// Этот метод никогда не начнет выполняться,
// поскольку дедлок произойдет в статическом
// конструкторе класса Program
>
>

* This source code was highlighted with Source Code Highlighter .

Причем, если обратиться к спецификации CLI, то в ней сказано, что дедлок возможен при явном или не явном вызове блокирующей операции внутри статического конструктора. На практике это означает, что попытка блокировки потока внутри статического конструктора, например, из-за использования именованного мьютекса в некоторых случаях может приводить к дедлоку.

Бага в реальном приложении

Вся эта чепуха, связанная со временем вызова инициализаторов полей и дедлоками в статических конструкторах, может показаться высосанной из пальца и маловероятной в реальных приложениях. Если честно, я был такого же мнения еще неделю назад, до того, как провел целый день в отладке одного весьма неприятного бага.

Итак, вот симптомы реальной проблемы, с которой я столкнулся. У нас есть сервис, который прекрасно работает в консольном режиме, а также не менее прекрасно работает в виде сервиса, если собрать его в Debug-е. Однако если собрать его в релизе, то он запускается через раз: один раз запускается успешно, а во второй раз запуск падает по тайм-ауту (по умолчанию SCM прибивает процесс, если сервис не запустился за 30 секунд).

В результате отладки было найдено следующее. (1) У нас есть класс сервиса, в конструкторе которого происходит создание счетчиков производительности; (2) класс сервиса реализован в виде синглтона с помощью инициализации статического поля без явного статического конструктора, и (3) этот синглтон использовался напрямую в методе Main для запуска сервиса в консольном режиме:

// Класс сервиса
partial class Service : ServiceBase
// "Кривоватая" реализаци Синглтона. Нет статического конструктора
public static readonly Service instance = new Service();
public static Service Instance < get < return instance; >>

public Service()
InitializeComponent();

// В конструкторе инициализирутся счетчики производительности
var counters = new CounterCreationDataCollection();

if (PerformanceCounterCategory.Exists(category))
PerformanceCounterCategory.Delete(category);

PerformanceCounterCategory.Create(category, description,
PerformanceCounterCategoryType.SingleInstance, counters);
>

// Метод запуска сервиса
public void Start()
<>

>

* This source code was highlighted with Source Code Highlighter .

Заключение

Дополнительные ссылки

(**) Один мой коллега свято верит в то, что книги и статьи читать не имеет смысла, поскольку их пишут те, кто ничего не смыслит в настоящей разработке ПО. Поэтому он считает, что существует только одна «настоящая» реализация синглтона, и что нужно добавлять пустой финализатор всем классам, реализующим интерфейс IDisposable.

(***) Если интересно, какие такие проблемы таятся в изменяемых значимых типах, то вполне подойдет предыдущая заметка «О вреде изменяемых значимых типов», ну а если интересно, что же такого плохого в вызове Thread.Abort, то тут есть даже две заметки: «О вреде вызова Thread.Abort», а также перевод интересной статьи Криса Селлза «Изучение ThreadAbortExcpetion с помощью Rotor».

В этой статье показано, как определить и использовать определяемые пользователем ссылочные типы и типы значений в C++/CLI.

Создание экземпляра объекта

Ссылочные типы (ref) можно создать только в управляемой куче, а не в стеке или в собственной куче. Типы значений можно создать в стеке или управляемой куче.

Неявно абстрактные классы

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

  • базовый тип класса является интерфейсом и
  • класс не реализует все функции-члены интерфейса.

Возможно, не удается создать объекты из класса, производного от интерфейса. Причина может быть в том, что класс неявно абстрагируется. Дополнительные сведения об абстрактных классах см. в разделе "Абстрактные".

В следующем примере кода показано, что MyClass невозможно создать экземпляр класса, так как функция MyClass::func2 не реализована. Чтобы включить компиляцию примера, раскомментируйте MyClass::func2 его.

Видимость типов

Вы можете управлять видимостью типов среды CLR. При ссылке на сборку вы можете контролировать, видимы ли типы в сборке или невидимы за пределами сборки.

По умолчанию до Visual Studio 2005 собственные типы имели общедоступную доступность за пределами сборки. Включите предупреждение компилятора (уровень 1) C4692 , чтобы узнать, где неправильно используются частные собственные типы. Используйте make_public pragma, чтобы предоставить доступ к собственному типу в файле исходного кода, который нельзя изменить.

Дополнительные сведения см. в разделе Директива using.

Выходные данные

Теперь давайте перезаписаем предыдущий пример, чтобы он был создан в виде библиотеки DLL.

В следующем примере показано, как получить доступ к типам за пределами сборки. В этом примере клиент использует компонент, встроенный в предыдущий пример.

Выходные данные

Видимость элемента

Вы можете сделать доступ к члену открытого класса из одной сборки, отличной от доступа к ней извне сборки с помощью пар описателей public доступа, protected и private

В этой таблице приведены сведения о влиянии различных описателей доступа:

Описатель Действие
public Член доступен внутри и за пределами сборки. Дополнительные сведения см. в разделе public .
private Член недоступен как внутри, так и за пределами сборки. Для получения дополнительной информации см. private .
protected Член доступен внутри и за пределами сборки, но только для производных типов. Дополнительные сведения см. в разделе protected .
internal Член является общедоступным внутри сборки, но закрытым за пределами сборки. internal — контекстно-зависимое ключевое слово. Дополнительные сведения см. в статье Context-Sensitive Keywords (C++/CLI and C++/CX) (Контекстно-зависимые ключевые слова (C++/CLI и C++/CX)).
public protected -или- protected public Член является общедоступным внутри сборки, но защищен за пределами сборки.
private protected -или- protected private Член защищен внутри сборки, но закрытый за пределами сборки.

В следующем примере показан открытый тип с элементами, объявленными с помощью разных описателей доступа. Затем он показывает доступ к этим членам из сборки.

Выходные данные

Теперь давайте создадим предыдущий пример в виде библиотеки DLL.

В следующем примере используется компонент, созданный в предыдущем примере. В нем показано, как получить доступ к членам извне сборки.

Выходные данные

Открытые и частные собственные классы

На собственный тип можно ссылаться из управляемого типа. Например, функция в управляемом типе может принимать параметр, тип которого является собственной структурой. Если управляемый тип и функция являются общедоступными в сборке, собственный тип также должен быть открытым.

Затем создайте файл исходного кода, который использует собственный тип:

Теперь скомпилируйте клиент:

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

Тип СРЕДЫ CLR , например класс или структуру, может иметь статический конструктор, который можно использовать для инициализации статических элементов данных. Статический конструктор вызывается по крайней мере один раз и вызывается до первого обращения к любому статическому элементу типа.

Конструктор экземпляра всегда выполняется после статического конструктора.

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

Определите статический конструктор как частную функцию-член, так как она должна вызываться только средой CLR.

Выходные данные

Семантика указателя this

При использовании C++\CLI для определения типов this указатель в ссылочный тип имеет дескриптор типа. Указатель this в типе значения имеет тип внутреннего указателя.

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

Выходные данные

Функции скрытия по сигнатуре

В стандартном C++ функция в базовом классе скрывается функцией, которая имеет то же имя в производном классе, даже если функция производного класса не имеет такого же типа или числа параметров. Это называется семантикой скрытия по имени . В ссылочных типах функция в базовом классе скрывается только функцией в производном классе, если имя и список параметров совпадают. Она называется семантикой скрытия по сигнатуре .

Класс считается классом скрытия по сигнатуре, когда все его функции помечены в метаданных как hidebysig . По умолчанию все классы, созданные в разделе /clr , имеют hidebysig функции. Если класс имеет hidebysig функции, компилятор не скрывает функции по имени в каких-либо прямых базовых классах, но если компилятор обнаруживает класс hide-by-name в цепочке наследования, он продолжает это поведение скрытия по имени.

При семантике скрытия по сигнатуре при вызове функции в объекте компилятор определяет наиболее производный класс, содержащий функцию, которая может удовлетворить вызов функции. Если в классе есть только одна функция, которая удовлетворяет вызову, компилятор вызывает ее. Если в классе существует несколько функций, которые могут удовлетворить вызов, компилятор использует правила разрешения перегрузки для определения вызываемой функции. Дополнительные сведения о правилах перегрузки см. в разделе "Перегрузка функций".

Для заданного вызова функции функция в базовом классе может иметь сигнатуру, которая делает ее немного лучше, чем функция в производном классе. Однако если функция была явно вызвана для объекта производного класса, вызывается функция в производном классе.

Поскольку возвращаемое значение не считается частью сигнатуры функции, функция базового класса скрывается, если она имеет то же имя и принимает тот же вид и число аргументов как функцию производного класса, даже если она отличается типом возвращаемого значения.

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

Выходные данные

В следующем примере показано, что компилятор Microsoft C++ вызывает функцию в самом производном классе, даже если преобразование требуется для сопоставления одного или нескольких параметров, а не вызывает функцию в базовом классе, который лучше подходит для вызова функции.

Выходные данные

В следующем примере показано, что можно скрыть функцию, даже если базовый класс имеет ту же сигнатуру, что и производный класс.

Выходные данные

Конструкторы копии

Стандарт C++ говорит, что конструктор копирования вызывается при перемещении объекта, таким образом, что объект создается и уничтожается по одному адресу.

Однако если функция, скомпилированная в MSIL, вызывает собственную функцию, в которой собственный класс (или несколько) передается по значению и где собственный класс имеет конструктор копирования или деструктор, конструктор копирования не вызывается, а объект уничтожается по другому адресу, чем при его создании. Такое поведение может вызвать проблемы, если класс имеет указатель на себя или если код отслеживает объекты по адресу.

Дополнительные сведения см. в разделе /clr (компиляция среды CLR).

В следующем примере показано, когда конструктор копирования не создается.

Выходные данные

Деструкторы и методы завершения

Деструкторы в ссылочных типах выполняют детерминированную очистку ресурсов. Методы завершения очищают неуправляемые ресурсы и могут вызываться детерминированным деструктором или недетерминированным сборщиком мусора. Сведения о деструкторах в стандартном C++см. в разделе "Деструкторы".

Сборщик мусора СРЕДЫ CLR удаляет неиспользуемые управляемые объекты и освобождает память, когда они больше не требуются. Однако тип может использовать ресурсы, которые сборщик мусора не знает, как освободить. Эти ресурсы называются неуправляемыми ресурсами (например, дескрипторами собственных файлов). Мы рекомендуем освободить все неуправляемые ресурсы в методе завершения. Сборщик мусора освобождает управляемые ресурсы недетерминированно, поэтому небезопасно ссылаться на управляемые ресурсы в методе завершения. Это потому, что возможно, сборщик мусора уже очистил их.

Метод завершения Visual C++ не совпадает с методом Finalize . (Документация по CLR использует метод завершения и Finalize метод синонимов). Метод Finalize вызывается сборщиком мусора, который вызывает каждый метод завершения в цепочке наследования классов. В отличие от деструкторов Visual C++, вызов метода завершения производного класса не приводит к вызову компилятора метода завершения во всех базовых классах.

Так как компилятор Microsoft C++ поддерживает детерминированный выпуск ресурсов, не пытайтесь реализовать Dispose или Finalize методы. Однако если вы знакомы с этими методами, вот как метод завершения Visual C++ и деструктор, вызывающий сопоставление метода завершения с шаблоном Dispose :

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

Компилятор Microsoft C++ позволяет определению деструктора деструктору детерминированно очищать объекты. Деструктор используется для освобождения всех ресурсов, которые требуется детерминировать. Если метод завершения присутствует, вызовите его из деструктора, чтобы избежать дублирования кода.

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

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

Вызов деструктора подавляет с помощью SuppressFinalizeзавершения объекта. Если деструктор не вызывается, метод завершения типа в конечном итоге будет вызываться сборщиком мусора.

Вы можете повысить производительность, вызвав деструктор для детерминированной очистки ресурсов объекта, а не позволяя среде CLR недетерминированно завершить объект.

Код, написанный на Visual C++ и скомпилированный с помощью /clr деструктора типа, если:

Объект, созданный с помощью семантики стека, выходит из области действия. Дополнительные сведения см. в разделе Семантика стека C++ для ссылочных типов.

Исключение возникает во время создания объекта.

Объект является членом объекта, деструктор которого выполняется.

Вы вызываете оператор delete для дескриптора (handle to Object Operator (^)).

Вы явно вызываете деструктор.

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

При вызове Dispose(void) типа.

Если вы не используете семантику стека для ссылочных типов и создаете объект ссылочного типа в управляемой куче, используйте синтаксис try-finally , чтобы убедиться, что исключение не препятствует запуску деструктора.

Если у типа есть деструктор, компилятор создает Dispose метод, реализующий IDisposable. Если тип, написанный на Visual C++ и имеющий деструктор, используемый с другого языка, вызов IDisposable::Dispose этого типа приводит к вызову деструктора типа. Если тип используется из клиента Visual C++, вы не можете напрямую вызвать Dispose деструктор. Вместо этого вызовите деструктор с помощью delete оператора.

Если тип имеет метод завершения, компилятор создает Finalize(void) метод, который переопределяет Finalize.

Если тип имеет метод завершения или деструктор, компилятор создает Dispose(bool) метод в соответствии с шаблоном конструктора. (Дополнительные сведения см. в разделе "Шаблон удаления"). Не удается явно создать или вызвать Dispose(bool) в Visual C++.

Если тип имеет базовый класс, соответствующий шаблону конструктора, деструкторы для всех базовых классов вызываются при вызове деструктора для производного класса. (Если тип написан в Visual C++, компилятор гарантирует, что типы реализуют этот шаблон.) Другими словами, деструктор ссылочного класса цепочек к его базам и членам, указанным стандартом C++. Сначала выполняется деструктор класса. Затем деструкторы для его членов выполняются в обратном порядке, в котором они были построены. Наконец, деструкторы для его базовых классов выполняются в обратном порядке, в котором они были созданы.

Деструкторы и методы завершения не допускаются внутри типов значений или интерфейсов.

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

После выполнения метода завершения объекта методы завершения в любых базовых классах также вызываются, начиная с наименее производного типа. Методы завершения для элементов данных не автоматически связаны с методом завершения класса.

Если метод завершения удаляет собственный указатель в управляемом типе, необходимо убедиться, что ссылки на собственный указатель или через него не собираются преждевременно. Вызовите деструктор управляемого типа вместо использования KeepAlive.

Во время компиляции можно определить, имеет ли тип метод завершения или деструктор. Дополнительные сведения см. в статье Compiler Support for Type Traits (C++/CLI and C++/CX) (Поддержка характеристик типов компилятором (C++/CLI and C++/CX)).

В следующем примере показаны два типа: один из них содержит неуправляемые ресурсы, а другой — управляемые ресурсы, которые освобождаются детерминированным образом.

When I have class containing a static constructor, is that constructor called when the assembly containing the class is first loaded or when the first reference to that class is hit?

5 Answers 5

You can now choose to sort by Trending, which boosts votes that have happened recently, helping to surface more up-to-date answers.

Trending is based off of the highest score sort and falls back to it if no posts are trending.

When the class is accessed for the first time.

A static constructor is used to initialize any static data, or to perform a particular action that needs performed once only. It is called automatically before the first instance is created or any static members are referenced.

Interesting that it says "before the first instance is created or any static members are referenced". There's some leeway there in when it actually gets invoked.

A static constructor is used to initialize any static data NO. Better to use static initializer to initialize static stuff.

Static constructor is guaranteed to be executed immediately before the first reference to a member of that class - either creation of instance or own static method/property of class.

Note that static initilaizers (if there is no static constructor) guaranteed to be executed any time before first reference to particular field.

Following question stackoverflow.com/questions/32525628/… demonstrate case when "immediate" behavior is quite obvious.

I actually just had the case where a static constructor was called right before the Main method of a console application even began executing!

The static constructor is called before you use anything in the class, but exactly when that happens is up to the implementation.

It's guaranteed to be called before the first static member is accessed and before the first instance is created. If the class is never used, the static constructor is not guaranteed to be called at all.

In case static method is called from parent class, static constructor will not be called, althogh it is explicitly specified. Here is an example b constructor is not called if b.methoda() is called.

There seems to be a gotcha with static constructors that is answered elsewhere but took a while to digest into a simple explanation. All the docs and explanations claim the static constructor/intializers are "guaranteed" to run before the first class is instantiated or the first static field is referenced. The gotcha comes in when you try to put a static singleton in the class that creates an instance of itself (chicken/egg). In this case the static constructor ends up being called after the instance constructor - and in my case the instance constructor contained code that relied on some static data.

(the answer for me was to put the singleton in a separate class or manually initialize the static data in the instance constructor before it is required)

Linked

Related

Hot Network Questions

To subscribe to this RSS feed, copy and paste this URL into your RSS reader.

Site design / logo © 2022 Stack Exchange Inc; user contributions licensed under cc by-sa. rev 2022.6.23.42447

Does it exist in C++? If yes, please explain it with an example.

If it's addressed to a C++ programmer, it should explain not ask what's meant by static constructor as it's not C++ terminology. For example, it might be asking if the constructor could have static linkage, or could be prefixed with the static keyword to some achieve unspecified behaviour.

13 Answers 13

You can now choose to sort by Trending, which boosts votes that have happened recently, helping to surface more up-to-date answers.

Trending is based off of the highest score sort and falls back to it if no posts are trending.

C++ doesn’t have static constructors but you can emulate them using a static instance of a nested class.

I guess that class constructor should be a friend of the class has_static_constructor to do anything useful? Otherwise it's just yet another static member.

@davka good remark. In fact, nesting isn’t particularly useful (other than constraining the scope, always a good thing).

You do have to be VERY CAREFUL about what you put in such a non-local static object constructor. When this constructor will run is non-deterministic and it will be very early in the boot process. A thorough explanation can be found in Scott Meyer's Effective C++ (Item 47 in the 2nd edition) on NLSO initialization. This can really bite you in the embedded world where the RTOS won't be available when the constructor runs.

@Tod Very valid comment. Unfortunately, having lazy loading is quite a bit more complicated. Essentially I would solve it with a static unique_ptr to a nested class which holds all the “static” members as, in fact, non-static members, and which is initialised to 0 and reset to a valid pointer once it’s first accessed.

For further question and interest you can read this topic:

Since we do not technically have static constructors in C++, you have to decide whether it is worth it to do something tricky to force the issue (e.g. using a static instance of a nested class), or to just slightly restructure your code to call a static initializer early in your program's life.

I like this approach better; as a silver lining, it takes the non- out of non-deterministic initialization.

There is one gotcha though -- this technique is insufficient if you are trying to initialize static const variables. For static const variables, you will have to make them private to the class and provide getters for outsiders to read them.

Note: I updated this code -- it compiles and runs successfully with no warnings via:

Please explain to me the use of static constructor. Why and when would we create a static constructor and is it possible to overload one?

It should be noted that there are static methods of construction (for an example, look up the Singleton Design Pattern) which are used to hide the actual constructors used to instanciate the class. This gives the author more control about how their class is used.

8 Answers 8

You can now choose to sort by Trending, which boosts votes that have happened recently, helping to surface more up-to-date answers.

Trending is based off of the highest score sort and falls back to it if no posts are trending.

No you can't overload it; a static constructor is useful for initializing any static fields associated with a type (or any other per-type operations) - useful in particular for reading required configuration data into readonly fields, etc.

It is run automatically by the runtime the first time it is needed (the exact rules there are complicated (see "beforefieldinit"), and changed subtly between CLR2 and CLR4). Unless you abuse reflection, it is guaranteed to run at most once (even if two threads arrive at the same time).

thanks for reply. can you please provide me more details about your sentence "Unless you abuse reflection, it is guaranteed to run at most once".. what can do with reflection regarding static constructor..

Hi @MarcGravell, please clarify what's the difference between CLR2 and CLR4 static constructors' behavior. And also, does it mean that static constructors are thread safe?

@Johnny_D pretty sure there are conditions where they can be deferred later in CLR4 - the exact scenario would need some digging. Re thread-safe: in sane scenarios, yes - basically. If you abuse them with reflection: not so much

  • A static constructor does not take access modifiers or have parameters.

  • A static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced.

  • A static constructor cannot be called directly.

  • The user has no control on when the static constructor is executed in the program.

  • A typical use of static constructors is when the class is using a log file and the constructor is used to write entries to this file.

  • Static constructors are also useful when creating wrapper classes for unmanaged code, when the constructor can call the LoadLibrary method.

Static constructors are also very useful when you have static fields that rely upon each other such that the order of initialization is important. If you run your code through a formatter/beautifier that changes the order of the fields then you may find yourself with null values where you didn't expect them.

Example: Suppose we had this class:

When you access fullUr , it will be "http://www.example.com/foo/bar".

Months later you're cleaning up your code and alphabetize the fields (let's say they're part of a much larger list, so you don't notice the problem). You have:

Your fullUrl value is now just "http://www.example.com/" since urlFragment hadn't been initialized at the time fullUrl was being set. Not good. So, you add a static constructor to take care of the initialization:

Now, no matter what order you have the fields, the initialization will always be correct.

If you are using ReSharper then I believe it will warn you in this situation when attempting to set fullUrl before urlFragment has been initialised. Good example nonetheless.

Do we know that the static constructor will execute after the field intialization every time? I guess not because Andrew's answer above says "A static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced.".

@NoChance Are you asking if the static constructor can be confident the fields that are initialized directly (in the example above firstPart and urlFragment) will have been initialized before that static constructor is called? The answer is yes, as long as the fields are listed before the static constructor. From MS docs: If static field variable initializers are present in the class of the static constructor, they will be executed in the textual order in which they appear in the class declaration immediately prior to the execution of the static constructor.

@NoChance Re-reading my comment, I believe I misspoke when I said the fields with initializers need to precede the static constructor. They will be initialized before the static constructor is executed regardless of where they are in the file.

1.It can only access the static member(s) of the class.

Reason : Non static member is specific to the object instance. If static constructor are allowed to work on non static members it will reflect the changes in all the object instance, which is impractical.

2.There should be no parameter(s) in static constructor.

Reason: Since, It is going to be called by CLR, nobody can pass the parameter to it. 3.Only one static constructor is allowed.

Reason: Overloading needs the two methods to be different in terms of method/constructor definition which is not possible in static constructor.

4.There should be no access modifier to it.

Reason: Again the reason is same call to static constructor is made by CLR and not by the object, no need to have access modifier to it

you can use static constructor to initializes static fields. It runs at an indeterminate time before those fields are used. Microsoft's documentation and many developers warn that static constructors on a type impose a substantial overhead.
It is best to avoid static constructors for maximum performance.
update: you can't use more than one static constructor in the same class, however you can use other instance constructors with (maximum) one static constructor.

It may be exceptionally obvious, but I think this answer hits a key point only implicit in, eg, marc's answer -- Static constructors are not in lieu of instance constructors. They are called once, before the first instance is created, to set up static properties, etc. Instance constructors keep on working like they've always operated, setting up stuff particular to that instance. static constructor : static class concerns :: instance constructor : instance level concerns Makes some sense. ;^)

Why and when would we create a static constructor .

One specific reason to use a static constructor is to create a 'super enum' class. Here's a (simple, contrived) example:

You'd use it very similarly (in syntactical appearance) to any other enum:

The advantage of this over a regular enum is that you can encapsulate related info easily. One disadvantage is that you can't use these values in a switch statement (because it requires constant values).

Static constructor called only the first instance of the class created. and used to perform a particular action that needs to be performed only once in the life cycle of the class.

A static constructor is used to initialize any static data, or to perform a particular action that needs to be performed only once. It is called automatically before the first instance is created or any static members are referenced.

Remarks

Static constructors have the following properties:

[!Note] Though not directly accessible, the presence of an explicit static constructor should be documented to assist with troubleshooting initialization exceptions.

Читайте также: