Инициализаторы конструкторов

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

· инициализатор конструктора экземпляров вида base(список аргументовopt) побуждает вызов конструктора экземпляров из прямого базового класса. Этот конструктор выбирается с помощью списка аргументов и правил разрешения перегрузки в §7.5.3. Набор конструкторов экземпляров – кандидатов включает все доступные конструкторы экземпляров, содержащиеся в прямом базовом классе, или конструктор по умолчанию (§10.11.4), если в прямом базовом классе не объявлены конструкторы экземпляров. Если этот набор пуст или если не удается идентифицировать один наилучший конструктор экземпляров, возникает ошибка времени компиляции.

· инициализатор конструктора экземпляров вида this(список аргументовopt) побуждает вызов конструктора экземпляров из самого класса. Конструктор выбирается с помощью списка аргументов и правил разрешения перегрузки в §7.5.3. Набор конструкторов экземпляров – кандидатов состоит из всех доступных конструкторов экземпляров, объявленных в самом классе. Если этот набор пуст или если не удается идентифицировать один наилучший конструктор экземпляров, возникает ошибка времени компиляции. Если объявление конструктора экземпляров включает инициализатор конструктора, вызывающий сам конструктор, возникает ошибка времени компиляции.

Если у конструктора экземпляров нет инициализатора конструктора, неявно предоставляется инициализатор конструктора вида base(). Таким образом, объявление конструктора экземпляров вида

C(...) {...}

в точности эквивалентно

C(...): base() {...}

Область видимости параметров, заданных списком формальных параметров объявления конструктора экземпляров, включает инициализатор конструктора этого объявления. Таким образом, инициализатору конструктора разрешен доступ к параметрам конструктора. Пример:

class A
{
public A(int x, int y) {}
}

class B: A
{
public B(int x, int y): base(x + y, x - y) {}
}

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

Инициализаторы переменных экземпляров

Если у конструктора экземпляра нет инициализатора конструктора или есть инициализатор конструктора вида base(...), этот конструктор неявно выполняет инициализации, указанные инициализаторами переменных полей экземпляра, объявленных в его классе. Это соответствует последовательности присваиваний, выполняемых непосредственно после входа в конструктор и перед неявным вызовом конструктора прямого базового класса. Инициализаторы переменных выполняются в том текстовом порядке, в каком они представлены в объявлении класса.

Выполнение конструктора

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

Пример.

using System;

class A
{
public A() {
PrintFields();
}

public virtual void PrintFields() {}

}

class B: A
{
int x = 1;
int y;

public B() {
y = -1;
}

public override void PrintFields() {
Console.WriteLine("x = {0}, y = {1}", x, y);
}
}

В этом примере, если new B() используется для создания экземпляра B, создается следующий вывод:

x = 1, y = 0

Значение x равно 1, потому что инициализатор переменной выполняется до вызова конструктора экземпляров базового класса. Однако значение y равно 0 (значение по умолчанию для int), потому что присваивание для y выполняется только после возврата из конструктора базового класса.

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

using System;
using System.Collections;

class A
{
int x = 1, y = -1, count;

public A() {
count = 0;
}

public A(int n) {
count = n;
}
}

class B: A
{
double sqrt2 = Math.Sqrt(2.0);
ArrayList items = new ArrayList(100);
int max;

public B(): this(100) {
items.Add("default");
}

public B(int n): base(n – 1) {
max = n;
}
}

содержится несколько инициализаторов переменных, а также содержатся инициализаторы конструкторов обоих видов (base и this). Этот пример соответствует коду, показанному ниже, где каждый комментарий указывает автоматически вставляемый оператор (синтаксис, используемый для автоматически вставляемых вызовов конструктора не является допустимым, а служит только для пояснения механизма).

using System.Collections;

class A
{
int x, y, count;

public A() {
x = 1; // Variable initializer
y = -1; // Variable initializer
object(); // Invoke object() constructor
count = 0;
}

public A(int n) {
x = 1; // Variable initializer
y = -1; // Variable initializer
object(); // Invoke object() constructor
count = n;
}
}

class B: A
{
double sqrt2;
ArrayList items;
int max;

public B(): this(100) {
B(100); // Invoke B(int) constructor
items.Add("default");
}

public B(int n): base(n – 1) {
sqrt2 = Math.Sqrt(2.0); // Variable initializer
items = new ArrayList(100); // Variable initializer
A(n – 1); // Invoke A(int) constructor
max = n;
}
}

Конструкторы по умолчанию

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

protected C(): base() {}

или

public C(): base() {}

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

В этом примере

class Message
{
object sender;
string text;
}

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

class Message
{
object sender;
string text;

public Message(): base() {}
}

Закрытые конструкторы

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

public class Trig
{
private Trig() {} // Prevent instantiation

public const double PI = 3.14159265358979323846;

public static double Sin(double x) {...}
public static double Cos(double x) {...}
public static double Tan(double x) {...}
}

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

Наши рекомендации