JavaRush /Курси /C# SELF /Перевантаження конструкторів

Перевантаження конструкторів

C# SELF
Рівень 16 , Лекція 4
Відкрита

1. Вступ

Припустімо, у нас є клас Person:

public class Person
{
    public string Name;
    public int Age;
    
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

І ось ви вже майже геній — можна створювати людей з будь-яким імʼям і віком. Але трапляється, що інформації бракує. Користувач застосунку ввів лише імʼя, а вік зʼясується трохи пізніше. Або ви хочете інколи призначати «стандартний» вік за замовчуванням. Звісно, можна вигадати обхідні шляхи, але C# пропонує для такої задачі елегантне рішення — перевантаження конструкторів.

Перевантаження конструкторів означає, що в одному класі може бути кілька конструкторів з однаковим імʼям (назвою класу), але з різними списками параметрів.

Чим відрізняються ці списки? Вони можуть різнитися за:

  1. Кількістю параметрів: наприклад, один конструктор приймає 2 параметри, інший — 3.
  2. Типами параметрів: один приймає (string, int), інший — (string, decimal).
  3. Порядком параметрів: (string, int) і (int, string) — це різні сигнатури.

Найголовніше правило: компілятор розрізняє конструктори (і будь-які методи) за їхньою сигнатурою. Сигнатура включає імʼя конструктора (або методу) і список його параметрів (кількість, типи та порядок). Модифікатори доступу (public, private) і, для звичайних методів, тип, що повертається, не є частиною сигнатури. Конструктори не мають типу, що повертається, а їхнє імʼя завжди збігається з імʼям класу.

2. Перевантаження конструкторів: синтаксис і приклад


public class Cat
{
    public string Name;
    public string Color;

    // Конструктор без параметрів
    public Cat()
    {
        Name = "Безіменний кіт";
        Color = "Сірий";
    }

    // Конструктор з одним параметром
    public Cat(string name)
    {
        Name = name;
        Color = "Сірий";
    }

    // Конструктор з двома параметрами
    public Cat(string name, string color)
    {
        Name = name;
        Color = color;
    }
}

Використання:

Cat barsik = new Cat(); // "Безіменний кіт", "Сірий"
Cat murzik = new Cat("Мурзик"); // "Мурзик", "Сірий"
Cat ryzhik = new Cat("Рижик", "Рудий"); // "Рижик", "Рудий"

Якщо під час створення обʼєкта ви вказуєте параметри, C# автоматично визначить потрібний конструктор за їхньою кількістю та типом.

Це дуже зручно! Користувачу вашого класу не потрібно вказувати параметри, яких він не знає: якщо потрібен просто кіт — використовується конструктор без параметрів; якщо відоме імʼя — інший; якщо потрібен повний контроль над кольором — третій варіант.

3. Як працює виклик перевантажених конструкторів?

C# обирає потрібний конструктор автоматично на етапі компіляції, спираючись на тип і кількість переданих аргументів. Помилитися важко — якщо щось не так, компілятор одразу повідомить про помилку.

Спробуймо додати конструктор з цілим параметром:

public Cat(int age)
{
    Name = "Безіменний кіт";
    Color = "Сірий";
    // Додатковий код для віку
}

Тепер ми можемо створити ще й «котика за віком»:

Cat oldCat = new Cat(5); // Під виклик потрапить конструктор Cat(int age)
Виклик конструктора Який викличеться?
new Cat()
Без параметрів
new Cat("Мурка")
З одним рядковим параметром
new Cat("Рижик", "Рудий")
З двома рядковими параметрами
new Cat(10)
З одним цілим параметром

4. Внутрішні виклики конструкторів: ключове слово this

Буває, що різні конструктори виконують схожу (або однакову) роботу. Щоб не дублювати код, можна «викликати один конструктор з іншого». Для цього використовують ключове слово this.


public class Cat
{
    public string Name;
    public string Color;

    public Cat() : this("Безіменний кіт")
    {
        // Цей конструктор викликає Cat(string name)
    }

    public Cat(string name) : this(name, "Сірий")
    {
        // Цей конструктор викликає Cat(string name, string color)
    }

    public Cat(string name, string color)
    {
        Name = name;
        Color = color;
    }
}

У результаті:

  • new Cat() викликає Cat(string name), який викликає Cat(string name, string color).
  • Усі «дороги ведуть до Рима»: уся ініціалізація зосереджена в одному «головному» конструкторі.

Такий підхід називається constructor chaining (ланцюжок виклику конструкторів).

5. Приклади з життя

Створімо клас герой (Hero) для гри, у якого є імʼя та рівень. Ось як ми його могли б написати:

З одним конструктором:

public class Hero
{
    public string Name;
    public int Level;

    public Hero(string name, int level)
    {
        Name = name;
        Level = level;
    }
}

А тепер дозволимо створювати його по-різному!

Кілька конструкторів:

public class Hero
{
    public string Name;
    public int Level;

    // Якщо нічого не вказано, нехай герой буде "Безіменний 1-го рівня"
    public Hero() : this("Безіменний", 1)
    {
    }

    // Якщо знаємо тільки ім'я, рівень за замовчуванням — 1
    public Hero(string name) : this(name, 1)
    {
    }

    // Найголовніший конструктор з двома параметрами
    public Hero(string name, int level)
    {
        Name = name;
        Level = level;
    }
}

Тепер різними способами можна створити героя:

Hero h1 = new Hero();               // "Безіменний", 1
Hero h2 = new Hero("Артур");        // "Артур", 1
Hero h3 = new Hero("Лора", 10);     // "Лора", 10

6. Деталі реалізації: підводні камені та нюанси

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

Hero h = new Hero(); // Помилка, якщо немає конструктора без параметрів!
спричинить помилку компіляції.

Друге: якщо у ланцюжку виклику конструкторів викликати не той конструктор, можна порушити логіку ініціалізації. Важливо зберігати послідовність: краще, щоб уся реальна робота відбувалася в найповнішому конструкторі, а інші лише передавали туди дані через this(...).

Третє: якщо параметри відрізняються лише типами (наприклад, Cat(string s) і Cat(object o)), можна випадково отримати плутанину під час виклику конструктора з аргументом типу null. Компілятор не завжди зможе визначити, який саме конструктор ви хочете викликати.

7. Перевантаження та ініціалізація полів

Часто в класі є поля, які потрібно обовʼязково задати. Перевантаження конструкторів допомагає робити це зручно, але ви самі визначаєте, які значення за замовчуванням є розумними для вашого класу.

Приклад із перевіркою:

public class Book
{
    public string Title;
    public int Year;

    // Книга за замовчуванням — "Без назви", 2000 рік
    public Book() : this("Без назви", 2000)
    {
    }
    
    public Book(string title) : this(title, 2000)
    {
    }

    public Book(string title, int year)
    {
        Title = title;
        Year = year;
    }
}

8. Перевантаження з різними наборами параметрів

Трапляються класи, у яких кількість конструкторів обчислюється десятками. Це не завжди ознака гарної архітектури (інколи краще використовувати «налаштувальні» методи або шаблони «Builder»), але для користувацьких обʼєктів, моделей, DTO — цілком робочий варіант.

Приклад: клас «Улюбленець», у якому можна явно вказати всі характеристики, а можна — лише найнеобхідніше.

public class Pet
{
    public string Name;
    public int Age;
    public string Type;
    public bool IsVaccinated;

    // Конструктор за замовчуванням
    public Pet() : this("NoName", 0, "Cat", false) { }

    // Мінімум інформації
    public Pet(string name, string type) : this(name, 0, type, false) { }

    // Повний конструктор
    public Pet(string name, int age, string type, bool isVaccinated)
    {
        Name = name;
        Age = age;
        Type = type;
        IsVaccinated = isVaccinated;
    }
}
Різниця між перевантаженням методу й конструктора
Звичайний метод Конструктор (у тому числі, перевантажений)
Можна викликати будь-коли Викликається лише під час створення обʼєкта (new)
Може повертати значення будь-якого типу Ніколи не повертає значення (тип не вказується)
Імʼя методу може бути довільним, зазвичай із дієсловом Імʼя завжди збігається з імʼям класу
Можна перевантажувати за параметрами Конструктори також перевантажуються за параметрами
1
Опитування
Ponyattya klasu ta ob'iekta, рівень 16, лекція 4
Недоступний
Ponyattya klasu ta ob'iekta
Klasy ta konstruktoru
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ