Создание конструкторов классов и создание экземпляров объектов
Помимо свойств и методов класса, определения классов включают конструкторы, которые используются для инициализации новых объектов (экземпляров классов).
Конструкторы классов
Конструктор класса — это метод с тем же именем, что и его тип (методы конструктора используют то же имя, что и класс).
Существует два типа конструкторов классов:
- Конструкторы экземпляров. Конструкторы экземпляров используются для создания и инициализации всех переменных поля экземпляра при создании объекта.
- Статические конструкторы. Статические конструкторы используются для инициализации статических данных или для выполнения определенного действия, которое необходимо выполнить только один раз. Статические конструкторы вызываются автоматически до создания первого экземпляра или ссылки на статические элементы.
Конструкторы классов — это конструкторы экземпляров по умолчанию.
Синтаксис конструктора экземпляра
Конструктор экземпляра объявляется с использованием того же имени, что и класс, и не включает возвращаемый тип. Подпись метода конструктора может включать дополнительный модификатор доступа, имя метода и его список параметров. Сигнатуры метода конструктора не включают тип возвращаемого значения.
В следующем примере показан простой конструктор для класса с именем Person:
public class Person
{
public Person()
{
// Field initialization and constructor logic goes here.
}
// Remaining implementation of Person class.
}
Классы могут иметь несколько конструкторов. Если класс имеет несколько конструкторов, конструкторы обычно принимают разные аргументы.
В следующем примере показан класс с именем Person с двумя конструкторами.
public class Person
{
public Person()
{
// Field initialization and constructor logic goes here.
string name = "John Doe";
Console.WriteLine($"Person created: {name}");
}
public Person(string fName, string lName)
{
string name = fName + " " + lName;
Console.WriteLine($"Person created: {name}");
}
// Remaining implementation of Person class.
}
Создание экземпляров объектов с помощью конструкторов классов
При создании экземпляра объекта с помощью ключевого слова new среда выполнения .NET вызывает конструктор связанного экземпляра в определении класса и выделяет память для объекта.
В следующем фрагменте кода класс Person определяет простой конструктор экземпляра. Класс Program включает метод Main, использующий оператор new для создания экземпляра Person с именем person1. Среда выполнения вызывает конструктор Person сразу после выделения памяти для нового объекта.
public class Person
{
public Person()
{
// Field initialization and constructor logic goes here.
}
}
static class Program
{
// the Main method is the entry point of the program.
static void Main()
{
Person person1 = new Person();
}
}
Конструкторы с параметрами и без параметров
Конструктор, который не принимает параметров, называется конструктором без параметров. Среда выполнения вызывает конструктор без параметров, если объект создается с помощью оператора new, а аргументы не предоставляются конструктору.
Заметка
Если класс не является статическим, компилятор C# добавляет классам без конструкторов публичный конструктор без параметров, чтобы позволить создание экземпляра класса.
Классы часто определяют конструкторы, которые принимают параметры. Конструкторы, которые принимают параметры, должны вызываться с помощью оператора new или базовой инструкции. Классы могут определять один или несколько конструкторов.
В следующем фрагменте кода показан класс с именем Person с тремя конструкторами:
public class Person
{
public Person()
{
// Field initialization and constructor logic goes here.
Console.WriteLine("An instance of the Person class is being instantiated without name or age parameters.");
}
public Person(string name)
{
// Field initialization and constructor logic goes here.
Console.WriteLine($"An instance of the Person class is being instantiated using a name ({name}) parameter.");
}
public Person(string name, int age)
{
// Field initialization and constructor logic goes here.
Console.WriteLine($"An instance of the Person class is being instantiated using name ({name}) and age ({age}) parameters.");
}
}
static class Program
{
// the Main method is the entry point of the program.
static void Main()
{
Person person1 = new Person();
Person person2 = new Person("Jane Doe");
Person person3 = new Person("John Doe", 30);
}
}
Классы без конструкторов
Если у класса нет явных конструкторов экземпляров, C# предоставляет конструктор без параметров, который можно использовать для создания экземпляра этого класса, как показано в следующем примере:
public class Person
{
public int age;
public string name = "unknown";
}
class Example
{
static void Main()
{
var person = new Person();
Console.WriteLine($"Name: {person.name}, Age: {person.age}");
// Output: Name: unknown, Age: 0
}
}
Этот конструктор инициализирует поля и свойства экземпляров в соответствии с соответствующими инициализаторами. Если поле или свойство не имеет инициализатора, его значение имеет значение по умолчанию типа поля или свойства. Если в классе объявлен хотя бы один конструктор экземпляра, C# не предоставляет конструктора без параметров.
Инициализация данных класса с помощью параметров конструктора
Параметры, передаваемые конструктору, являются локальными для конструктора. Параметры часто используются для инициализации полей данных класса.
В следующем фрагменте кода показан класс с именем Person с конструкторами, которые инициализируют поля personName и personAge:
public class Person
{
public string personName;
public string personAge;
public Person()
{
// Field initialization and constructor logic goes here.
personName = "unknown";
personAge = "unknown";
}
public Person(string name)
{
// Field initialization and constructor logic goes here.
personName = name;
personAge = "unknown";
}
public Person(string name, int age)
{
// Field initialization and constructor logic goes here.
personName = name;
personAge = age.ToString();
}
}
static class Program
{
// the Main method is the entry point of the program.
static void Main()
{
Person person1 = new Person();
Person person2 = new Person("Jane Doe");
Person person3 = new Person("John Doe", 30);
Console.WriteLine($"Person 1 Name: {person1.personName} Age: {person1.personAge}");
Console.WriteLine($"Person 2 Name: {person2.personName} Age: {person2.personAge}");
Console.WriteLine($"Person 3 Name: {person3.personName} Age: {person3.personAge}");
}
}
В предыдущем примере класс Person определен с тремя конструкторами. Первый конструктор инициализирует поля personName и personAge значением "unknown". Второй конструктор инициализирует поле personName значением, переданным в параметре name, и поле personAge значением "unknown". Третий конструктор инициализирует поля personName и personAge на значениям, переданным в параметрах name и age соответственно.
Так как поля являются общедоступными, доступ к ней можно получить непосредственно из метода Main. При выполнении кода создаются следующие выходные данные:
Person 1 Name: unknown Age: unknown
Person 2 Name: Jane Doe Age: unknown
Person 3 Name: John Doe Age: 30
Определения текста выражения
Если конструктор можно реализовать в виде одной инструкции, можно использовать определение текста выражения для назначения параметра члену класса при реализации конструктора.
Например, следующий конструктор инициализирует поле modelName со значением, переданным параметру model:
public class Car
{
public string modelName;
public Car(string model) => modelName = model;
}
Класс Car имеет одно общедоступное поле, modelName, которое имеет тип string. Поле modelName предназначено для хранения имени модели автомобиля.
Класс Car также включает конструктор, который принимает один строковый параметр с именем model. Конструктор использует определение текста выражения (обозначенное синтаксисом =>) для инициализации поля modelName со значением, переданным параметру model. Это означает, что при создании экземпляра нового объекта Car поле modelName задается значением, предоставленным в качестве аргумента конструктора.
Поскольку термин выражения подразумевает, что то, что находится справа от оператора =>, является выражением и не ограничивается простой операцией присваивания. Выражение может быть любым допустимым выражением C#, возвращающим значение.
В следующем фрагменте кода показано, как реализовать определение текста выражения, которое выполняет простое вычисление:
public class Employee
{
public int Salary;
public Employee() { }
public Employee(int annualSalary) => Salary = annualSalary;
public Employee(int weeklySalary, int numberOfWeeks) => Salary = weeklySalary * numberOfWeeks;
}
Этот класс можно создать с помощью любой из следующих инструкций:
Employee e1 = new Employee(30000);
Employee e2 = new Employee(500, 52);
Статические конструкторы
Статический конструктор используется для инициализации статических данных или выполнения определенного действия, которое необходимо выполнить только один раз. Он вызывается автоматически до создания первого объекта или ссылки на статические члены. Статический конструктор вызывается по крайней мере один раз.
В следующем фрагменте кода показана обновленная версия класса Person, реализующего статические поля и статический конструктор:
public class Person
{
public string personName;
public string personAge;
// Static field
public static string defaultName;
public static string defaultAge;
// Static constructor
static Person()
{
// Static field initialization
defaultName = "unknown";
defaultAge = "unknown";
}
public Person()
{
// Field initialization and constructor logic goes here.
personName = defaultName;
personAge = defaultAge;
}
public Person(string name)
{
// Field initialization and constructor logic goes here.
personName = name;
personAge = defaultAge;
}
public Person(string name, int age)
{
// Field initialization and constructor logic goes here.
personName = name;
personAge = age.ToString();
}
}
Обновленный класс Person содержит два поля экземпляра, personName и personAge, оба из которых имеют тип string. Эти поля хранят имя и возраст человека соответственно.
Класс также определяет два статических поля, defaultName и defaultAge, а также тип string. Статические поля используются для всех экземпляров класса и инициализированы только один раз. В этом случае статические поля используются для предоставления значений по умолчанию для полей personName и personAge.
Статический конструктор static Person() отвечает за инициализацию статических полей. Он задает для defaultName значение "неизвестно" и defaultAge значение "unknown". Статический конструктор вызывается автоматически перед созданием любого экземпляра класса или доступом к любым статическим элементам.
Класс Person включает три конструктора экземпляра:
Конструктор без параметров public Person() инициализирует поля personName и personAge со значениями статических полей defaultName и defaultAge. Это означает, что если аргументы не предоставляются при создании объекта Person, значения по умолчанию "неизвестны" используются как для имени, так и для возраста.
Конструктор public Person(string name) принимает один параметр, nameи инициализирует поле personName с этим значением. Поле personAge инициализировано со значением статического поля defaultAge. Этот конструктор позволяет создавать объект Person с указанным именем при использовании возраста по умолчанию.
Конструктор public Person(string name, int age) принимает два параметра, name и age. Он инициализирует поле personName со значением параметра name и полем personAge строковым представлением параметра age. Этот конструктор позволяет создавать объект Person с указанным name и age.
Свойства статических конструкторов
Статические конструкторы имеют следующие свойства:
Статический конструктор не принимает модификаторы доступа и не имеет параметров.
Класс может иметь только один статический конструктор.
Статические конструкторы не могут быть унаследованы или перегружены.
Статический конструктор не может вызываться напрямую и должен вызываться только средой CLR. Он вызывается автоматически.
Пользователь не имеет контроля над выполнением статического конструктора в программе.
Статический конструктор вызывается автоматически. Он инициализирует класс перед созданием первого экземпляра или обращением к любым статическим членам, объявленным в этом классе (а не базовых классах). Статический конструктор запускается перед конструктором экземпляра. Если инициализаторы статических полей присутствуют в классе статического конструктора, они выполняются в текстовом порядке, в котором они отображаются в объявлении класса. Инициализаторы выполняются непосредственно перед статическим конструктором.
Если вы не предоставляете статический конструктор для инициализации статических полей, все статические поля инициализированы в значение по умолчанию.
Если статический конструктор создает исключение, среда выполнения не вызывает его во второй раз, а тип остается неинициализируемым в течение времени существования домена приложения. Чаще всего исключение
TypeInitializationExceptionвозникает, когда статический конструктор не может создать экземпляр типа или необработанного исключения, возникающего в статическом конструкторе. Для статических конструкторов, не определенных явным образом в исходном коде, может потребоваться проверка кода промежуточного языка (IL).Наличие статического конструктора предотвращает добавление атрибута типа
BeforeFieldInit. Это ограничивает оптимизацию среды выполнения.Поле, объявленное как
static readonly, может быть назначено только в рамках его объявления или в статическом конструкторе. Если явный статический конструктор не требуется, инициализируйте статические поля в объявлении, а не через статический конструктор, чтобы лучше оптимизировать выполнение.Среда выполнения вызывает статический конструктор не более одного раза в одном домене приложения. Этот вызов выполняется в заблокированном регионе в зависимости от конкретного типа класса. В тексте статического конструктора не требуются дополнительные механизмы блокировки.
Заметка
Хотя он недоступен напрямую, необходимо задокументировать наличие явного статического конструктора, чтобы помочь в диагностике проблем с исключениями инициализации.