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

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

Он внимательно посмотрел на создание объектов и сказал:

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

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

// Создаем магов
Mage mage1 = new Mage();
mage1.Name = "Альберт";
mage1.Mana = 20;

Mage mage2 = new Mage();
mage2.Name = "Белла";
mage2.Mana = 30;

Задача: Вы решаете улучшить систему создания объектов. Как бы вы предложили упростить процесс создания магов и воинов внутри метода Main, чтобы упростить жизнь другим разработчикам, которые будут создавать объекты на основе вашего класса?

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

Наше решение стало короче, но всё ещё есть проблема.

Mage mage1 = new Mage();
mage1.Construct("Альберт", 20);

Warrior warrior1 = new Warrior();
warrior1.Construct("Эдвард", 100);

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

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

Как раз для решения этой проблемы в языке C# используется конструктор.

Давайте, следуя определению, напишем конструктор для класса Mage:

public class Mage
{
    public string Name;
    public int Mana;

    // Конструктор класса
    public Mage(string name, int mana)
    {
        Name = name;
        Mana = mana;
    }

    public void CastAttackSpell()
    {
        if (Mana >= 5)
        {
            Mana -= 5;
            Console.WriteLine($"{Name} использует заклинание высшего круга магии. (Мана уменьшена на 5)");
        }
        else
        {
            Console.WriteLine($"Маг {Name} не может атаковать, не хватает маны");
        }
    }

    public void RestoreMana(int amount)
    {
        if (amount < 0)
        {
            Console.WriteLine("Нельзя восстановить отрицательное количество маны.");
            Mana += 0;
        }
        else
        {
            Mana += amount;

            if (Mana > 100)
            {
                Mana = 100;
            }

            Console.WriteLine($"{Name} восполнил ману на {amount}. Текущая мана: {Mana}.");
        }
    }
}

И для класса Warrior:

public class Warrior
{
    public string Name;
    public int Stamina;

    // Конструктор класса
    public Warrior(string name, int stamina)
    {
        Name = name;
        Stamina = stamina;
    }

    public void Attack()
    {
        if (Stamina >= 5)
        {
            Stamina -= 5;
            Console.WriteLine($"{Name} наносит сильный удар. (Выносливость уменьшена на 5)");
        }
        else
        {
            Console.WriteLine($"Воин {Name} устал и не может атаковать");
        }
    }
}

Теперь для создания объектов с заполненными полями нам достаточно передать нужные значения при создании объекта. Порядок передачи значений важен!

internal class Program
{
    static void Main(string[] args)
    {
        // Создаем объекты магов
        Mage mage1 = new Mage("Альберт", 20);
        Mage mage2 = new Mage("Белла", 30);

        mage1.CastAttackSpell();
        mage2.CastAttackSpell();

        // Создаем воина
        Warrior warrior1 = new Warrior("Эдвард", 100);

        warrior1.Attack();
    }
}

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

Было:

Mage mageAlbert = new Mage();
mage1.Name = "Альберт";
mage1.Mana = 20;

Стало:

Mage mage1 = new Mage("Альберт", 20);

Иногда при создании объектов мы понимаем, что у многих из них будет одинаковое повторяющееся значение. Например, мы можем решить, что по умолчанию у всех магов одинаковое количество маны — 50. Тогда мы можем задать это значение в конструкторе как значение параметра по умолчанию:

// Устанавливаем значение по умолчанию для магической энергии
public Mage(string name, int mana = 50)
{
    Name = name;
    Mana = mana;
}

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

Mage mage1 = new Mage("Merlin");           // Мана по умолчанию равна 50
Mage mage2 = new Mage("Gandalf", 80);      // Мана установлена в 80

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

public Mage(string name, int mana = 50)  // верно
public Mage(int mana = 50, string name)  // ошибка компиляции

На самом деле конструктор у нас уже был с самого начала! Мы всегда его вызывали, только он был пустым. Посмотрите: мы всегда создавали объект так:

Mage mage1 = new Mage();

Мы постоянно использовали ключевое слово new, и, как мы знаем, оно выделяет память и создаёт объект. Но у него есть ещё одна важная задача — оно вызывает конструктор класса.

Но у нас же не было конструктора класса!

Да, явно мы его не писали. Однако в C# есть важная особенность: если в классе не указан конструктор, компилятор создаёт его автоматически.

Код урока: Конструктор**

Конструктор — это специальный метод в классе, который вызывается автоматически при создании объекта. Имя конструктора всегда совпадает с именем класса. Конструктор не имеет возвращаемого значения (даже void не указывается).

Конструктор по умолчанию — конструктор без параметров, который компилятор создаёт автоматически, если в классе не объявлен ни один конструктор явно. Инициализирует поля значениями по умолчанию (0 для чисел, null для строк).

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

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

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

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

Обсуждение урока

0
Комментарии видны всем. Чтобы участвовать в обсуждении, войдите или зарегистрируйтесь.
Модерация сообщества

Пожаловаться на комментарий

Расскажите модераторам, что именно требует внимания.