Введение: Путешествие в мир объектов
Привет, юный программист! 👋 Сегодня мы начинаем самое интересное — знакомство с объектно-ориентированным программированием (ООП) в C#. Представь, что ты строишь город из LEGO. Обычное программирование — это когда ты собираешь всё из отдельных кирпичиков, а ООП — это когда у тебя есть готовые домики, машинки и деревья, которые ты просто соединяешь друг с другом. Удобно, правда?
В этом уроке мы узнаем, как создавать свои собственные «кирпичики» — классы и объекты, как давать им свойства (цвет, размер) и действия (ехать, прыгать). И самое главное — поймём, почему ООП делает код понятным и удобным для больших проектов.
Давай начнём с простого: представь, что ты описываешь собаку. У неё есть характеристики (имя, возраст, порода) и действия (лаять, бегать, есть). В ООП характеристики становятся свойствами, а действия — методами. Класс — это как чертёж собаки, а объект — конкретная собака по имени Бобик.
🎯 Цель урока: Научиться создавать свои классы, понимать разницу между классом и объектом, использовать конструкторы и методы.
1. Классы и объекты: фундамент ООП
Что такое класс?
Класс — это как шаблон или форма для печенья. Если у тебя есть форма в виде звезды, ты можешь сделать много звёздочек из теста. Класс — это форма, а объекты — это те самые звёздочки. В программировании класс описывает, какие данные (свойства) и действия (методы) будут у всех объектов этого класса.
Давай создадим простой класс Собака:
// Объявляем класс "Собака"
class Собака
{
// Свойства (характеристики)
public string Имя; // Имя собаки
public int Возраст; // Возраст в годах
public string Порода; // Порода
// Метод (действие)
public void Лаять()
{
Console.WriteLine("Гав-гав!");
}
}
Разберём код:
- class Собака — мы создаём новый тип данных, как конструктор LEGO
- public string Имя — свойство «Имя» типа строка (текст)
- public int Возраст — свойство «Возраст» типа целое число
- public void Лаять() — метод, который выводит звук лая
Ключевое слово public означает, что к этим членам класса можно обращаться откуда угодно (как к открытой двери). Если поставить private, доступ будет только внутри класса (как секретная комната).
Что такое объект?
Объект — это конкретный экземпляр класса. Если класс — это чертёж дома, то объект — сам дом, построенный по этому чертежу. Чтобы создать объект, нужно использовать ключевое слово new:
// Создаём объект "мояСобака" на основе класса "Собака"
Собака мояСобака = new Собака();
// Заполняем свойства
мояСобака.Имя = "Бобик";
мояСобака.Возраст = 3;
мояСобака.Порода = "Такса";
// Вызываем метод
мояСобака.Лаять(); // Выведет: Гав-гав!
// Выводим информацию о собаке
Console.WriteLine($"{мояСобака.Имя} - {мояСобака.Порода}, {мояСобака.Возраст} года");
// Выведет: Бобик - Такса, 3 года
Обрати внимание: new Собака() создаёт новый объект в памяти. Можно создать много собак:
Собака собака1 = new Собака();
собака1.Имя = "Шарик";
собака1.Возраст = 5;
Собака собака2 = new Собака();
собака2.Имя = "Мурка"; // Да, собака с кошачьим именем, почему нет?
собака2.Возраст = 2;
Console.WriteLine($"{собака1.Имя} и {собака2.Имя} дружат!");
// Выведет: Шарик и Мурка дружат!
Жизненная аналогия
Представь, что ты работаешь в кондитерской. У тебя есть форма для печенья (класс). С её помощью ты делаешь много печенек (объекты). У каждой печеньки есть форма (свойство), но они могут быть разного цвета (другие свойства). Ты можешь их продавать (метод).
Класс — это форма. Объект — печенька. Одна форма — много печенек. Вот и вся магия!
2. Конструкторы: как создавать объекты с готовыми данными
В прошлом примере мы сначала создавали объект, а потом заполняли свойства. Но что, если мы хотим задать всё сразу при создании? Для этого существуют конструкторы — специальные методы, которые вызываются автоматически при создании объекта.
Конструктор имеет то же имя, что и класс, и не возвращает никакого значения (даже void не пишем).
class Собака
{
public string Имя;
public int Возраст;
public string Порода;
// Конструктор с параметрами
public Собака(string имя, int возраст, string порода)
{
Имя = имя; // Записываем переданное имя в свойство
Возраст = возраст; // То же для возраста
Порода = порода; // И для породы
}
// Конструктор без параметров (по умолчанию)
public Собака()
{
Имя = "Неизвестно";
Возраст = 0;
Порода = "Неизвестна";
}
public void Лаять()
{
Console.WriteLine($"{Имя} говорит: Гав-гав!");
}
}
// Использование:
// Создаём с параметрами
Собака бобик = new Собака("Бобик", 3, "Такса");
бобик.Лаять(); // Выведет: Бобик говорит: Гав-гав!
// Создаём без параметров (используется второй конструктор)
Собака неизвестная = new Собака();
Console.WriteLine(неизвестная.Имя); // Выведет: Неизвестно
Видишь, как удобно? Можно сразу передать все данные, и объект готов к использованию. А если данных нет — конструктор по умолчанию задаёт стандартные значения.
Ещё пример с классом Игрок для игры:
class Игрок
{
public string Никнейм;
public int Уровень;
public int Здоровье;
// Конструктор
public Игрок(string никнейм)
{
Никнейм = никнейм;
Уровень = 1; // Новый игрок всегда 1 уровня
Здоровье = 100; // Полное здоровье
}
public void Атаковать(int урон)
{
Здоровье -= урон;
Console.WriteLine($"{Никнейм} получил {урон} урона. Осталось здоровья: {Здоровье}");
}
}
// Создаём игрока
Игрок герой = new Игрок("СуперКодер");
Console.WriteLine($"{герой.Никнейм}, уровень {герой.Уровень}, здоровье {герой.Здоровье}");
// Выведет: СуперКодер, уровень 1, здоровье 100
герой.Атаковать(30); // Выведет: СуперКодер получил 30 урона. Осталось здоровья: 70
3. Методы: действия объектов
Методы — это функции, которые принадлежат классу. Они описывают, что объект может делать. Методы могут принимать параметры и возвращать значения.
Давай создадим класс Калькулятор, который умеет складывать и вычитать:
class Калькулятор
{
// Метод, который принимает два числа и возвращает сумму
public int Сложить(int a, int b)
{
int результат = a + b;
return результат; // Возвращаем результат
}
// Метод для вычитания
public int Вычесть(int a, int b)
{
return a - b; // Можно сразу вернуть
}
// Метод, который ничего не возвращает, но выводит на экран
public void ПоказатьРезультат(string операция, int результат)
{
Console.WriteLine($"Результат {операции}: {результат}");
}
}
// Использование:
Калькулятор кальк = new Калькулятор();
int сумма = кальк.Сложить(10, 5);
кальк.ПоказатьРезультат("сложения", сумма); // Выведет: Результат сложения: 15
int разность = кальк.Вычесть(10, 5);
кальк.ПоказатьРезультат("вычитания", разность); // Выведет: Результат вычитания: 5
Обрати внимание на ключевое слово return — оно возвращает значение из метода. Если метод объявлен как void, он ничего не возвращает (просто выполняет действия).
Методы с параметрами по умолчанию
Можно задать значения параметров, которые будут использоваться, если их не передали:
class Приветствие
{
public void СказатьПривет(string имя = "Друг")
{
Console.WriteLine($"Привет, {имя}!");
}
}
Приветствие привет = new Приветствие();
привет.СказатьПривет("Анна"); // Выведет: Привет, Анна!
привет.СказатьПривет(); // Выведет: Привет, Друг!
4. Свойства: полный контроль над данными
Свойства — это не просто переменные, а специальные конструкции, которые позволяют контролировать доступ к данным. Они могут проверять значения или выполнять код при чтении/записи.
Давай создадим класс Счет для банковского счёта:
class Счет
{
private decimal баланс; // Приватное поле (скрыто от внешнего мира)
// Свойство для доступа к балансу
public decimal Баланс
{
get { return баланс; } // Чтение
private set { баланс = value; } // Запись (только внутри класса)
}
// Конструктор
public Счет(decimal начальныйБаланс)
{
if (начальныйБаланс >= 0)
баланс = начальныйБаланс;
else
баланс = 0;
}
// Метод для пополнения
public void Пополнить(decimal сумма)
{
if (сумма > 0)
{
баланс += сумма;
Console.WriteLine($"Счёт пополнен на {сумма}. Баланс: {баланс}");
}
}
// Метод для снятия
public void Снять(decimal сумма)
{
if (сумма > 0 && сумма <= баланс)
{
баланс -= сумма;
Console.WriteLine($"Снято {сумма}. Баланс: {баланс}");
}
else
{
Console.WriteLine("Недостаточно средств или неверная сумма");
}
}
}
// Использование:
Счет мойСчет = new Счет(1000);
Console.WriteLine(мойСчет.Баланс); // 1000
мойСчет.Пополнить(500); // Счёт пополнен на 500. Баланс: 1500
мойСчет.Снять(200); // Снято 200. Баланс: 1300
мойСчет.Снять(2000); // Недостаточно средств или неверная сумма
// мойСчет.Баланс = 5000; // Ошибка! Свойство Баланс имеет private set
Здесь value — это ключевое слово, которое означает значение, переданное при присваивании. Свойства защищают данные от некорректных изменений.
5. Статические члены: общее для всех объектов
Иногда нужно, чтобы свойство или метод принадлежали не конкретному объекту, а всему классу целиком. Например, общее количество созданных объектов. Для этого используется ключевое слово static.
class Пользователь
{
public string Имя;
public static int КоличествоПользователей = 0; // Статическое поле
public Пользователь(string имя)
{
Имя = имя;
КоличествоПользователей++; // Увеличиваем счётчик при создании
}
// Статический метод
public static void ПоказатьКоличество()
{
Console.WriteLine($"Всего пользователей: {КоличествоПользователей}");
}
}
// Создаём пользователей
Пользователь юзер1 = new Пользователь("Анна");
Пользователь юзер2 = new Пользователь("Иван");
Пользователь юзер3 = new Пользователь("Мария");
// Вызываем статический метод (через имя класса, не через объект!)
Пользователь.ПоказатьКоличество(); // Выведет: Всего пользователей: 3
// Статические члены доступны без создания объекта
Console.WriteLine(Пользователь.КоличествоПользователей); // 3
Статические методы часто используются для вспомогательных функций, например, математических расчётов.
class Математика
{
public static double Пи = 3.14159;
public static double ПлощадьКруга(double радиус)
{
return Пи * радиус * радиус;
}
}
// Используем без создания объекта
double площадь = Математика.ПлощадьКруга(5);
Console.WriteLine($"Площадь круга: {площадь}"); // Площадь круга: 78.53975
6. Инкапсуляция: защита данных
Инкапсуляция — это принцип, по которому данные объекта скрыты от внешнего мира, а доступ к ним осуществляется только через специальные методы (геттеры и сеттеры) или свойства. Это как пульт от телевизора: ты нажимаешь кнопки, но не знаешь, как внутри устроены микросхемы.
Зачем это нужно? Чтобы никто случайно не испортил данные. Например, возраст не может быть отрицательным, а имя — пустым.
class Человек
{
private string имя; // Приватное поле
private int возраст; // Приватное поле
// Свойство с проверкой
public string Имя
{
get { return имя; }
set
{
if (!string.IsNullOrEmpty(value)) // Проверяем, что имя не пустое
имя = value;
else
Console.WriteLine("Имя не может быть пустым!");
}
}
public int Возраст
{
get { return возраст; }
set
{
if (value >= 0 && value <= 150) // Проверяем возраст
возраст = value;
else
Console.WriteLine("Некорректный возраст!");
}
}
// Конструктор
public Человек(string имя, int возраст)
{
Имя = имя; // Используем свойства (с проверками)
Возраст = возраст;
}
public void Представиться()
{
Console.WriteLine($"Привет, я {имя}, мне {возраст} лет.");
}
}
// Использование:
Человек человек = new Человек("Анна", 25);
человек.Представиться(); // Привет, я Анна, мне 25 лет.
// Попробуем задать некорректные данные
человек.Возраст = -5; // Некорректный возраст!
человек.Имя = ""; // Имя не может быть пустым!
Console.WriteLine(человек.Возраст); // Выведет 25 (старое значение осталось)
7. Практические задания
Теперь твоя очередь! Попробуй выполнить эти задания самостоятельно. Если что-то не получается — перечитай урок или попроси помощи.
Задание 1: Класс «Книга»
Создай класс Книга со следующими элементами:
- Свойства:
Название(string),Автор(string),ГодИздания(int),КоличествоСтраниц(int) - Конструктор, который принимает все четыре параметра
- Метод
Описание(), который выводит информацию о книге в формате: «"Название" - Автор, Год издания, X страниц» - Метод
Сравнить(Книга другаяКнига), который выводит, какая книга толще (больше страниц)
Пример использования:
Книга книга1 = new Книга("Война и мир", "Лев Толстой", 1869, 1225);
Книга книга2 = new Книга("Маленький принц", "Антуан де Сент-Экзюпери", 1943, 96);
книга1.Описание(); // "Война и мир" - Лев Толстой, 1869, 1225 страниц
книга1.Сравнить(книга2); // "Война и мир" толще, чем "Маленький принц"
Задание 2: Класс «Таймер»
Создай класс Таймер, который симулирует обратный отсчёт:
- Приватное поле
оставшиесяСекунды(int) - Свойство
ОставшиесяСекундыс проверкой: значение не может быть отрицательным (если пытаются задать отрицательное, ставится 0) - Конструктор, который принимает начальное количество секунд
- Метод
Тик(), который уменьшает оставшиеся секунды на 1 и выводит сообщение: «Осталось: X секунд». Если время вышло, выводит «Время вышло!» - Метод
Сбросить(int новыеСекунды), который устанавливает новое значение (с проверкой)
Пример использования:
Таймер таймер = new Таймер(5);
таймер.Тик(); // Осталось: 4 секунд
таймер.Тик(); // Осталось: 3 секунд
// ... (вызываем ещё три раза)
таймер.Тик(); // Время вышло!
таймер.Сбросить(3);
таймер.Тик(); // Осталось: 2 секунд
Задание 3: Класс «Банкомат»
Создай класс Банкомат, который управляет выдачей денег:
- Приватное поле
баланс(decimal) — сколько денег в банкомате - Свойство
Баланстолько для чтения (только get) - Конструктор, который задаёт начальный баланс (не может быть отрицательным)
- Метод
Внести(decimal сумма)— добавляет деньги (сумма должна быть положительной) - Метод
Снять(decimal сумма)— снимает деньги, если достаточно средств. Выводит сообщение об успехе или ошибке - Статическое поле
ОбщаяСуммаВсехБанкоматов(decimal), которое увеличивается при создании каждого банкомата и при внесении денег - Статический метод
ПоказатьОбщуюСумму(), который выводит общую сумму всех банкоматов
Пример использования:
Банкомат банкомат1 = new Банкомат(10000);
Банкомат банкомат2 = new Банкомат(5000);
банкомат1.Внести(2000);
банкомат2.Снять(3000); // Успешно
Банкомат.ПоказатьОбщуюСумму(); // Общая сумма всех банкоматов: 14000
Console.WriteLine(банкомат1.Баланс); // 12000
Console.WriteLine(банкомат2.Баланс); // 2000
Заключение
Поздравляю! 🎉 Ты сделал первый шаг в мир объектно-ориентированного программирования. Теперь ты знаешь:
- Что такое классы и объекты
- Как создавать конструкторы
- Как писать методы и свойства
- Зачем нужна инкапсуляция
- Что такое статические члены
ООП — это мощный инструмент, который используют все профессиональные программисты. С его помощью можно создавать большие и сложные программы, которые легко понимать и изменять. В следующих уроках мы узнаем про наследование, полиморфизм и другие крутые вещи.
А пока — попробуй выполнить задания. Если что-то непонятно, перечитай примеры или напиши свой код — практика лучше всего помогает разобраться. Удачи!
💡 Совет: Попробуй придумать свой класс, например, «Смартфон», «ИгровойПерсонаж» или «Рецепт». Опиши его свойства и методы — это поможет закрепить материал.