Классы в C#: как создавать, использовать и проектировать

Классы — базовый способ моделировать данные и поведение в C#. В статье показано, как объявлять классы, поля, свойства, конструкторы, методы и создавать объекты. Также описаны альтернативы, лучшие практики, контрольные списки и критерии приёмки.
Введение
Объектно-ориентированное программирование позволяет думать о программе в терминах реальных объектов: у них есть состояние (поля/свойства) и поведение (методы). В C# класс — это ссылка на тип (reference type). Пока вы не создадите экземпляр класса через оператор new, переменная такого типа может содержать null.
Ниже — подробное руководство с примерами и полезными практиками для безопасной и ясной работы с классами в C#.
Объявление класса
Чтобы объявить класс в C#, нужны несколько компонентов:
- модификатор доступа;
- ключевое слово class;
- имя класса;
- пара фигурных скобок, внутри которых находятся поля, конструкторы и методы.
Пример минимального объявления:
internal class Customer { }Этот класс доступен другим типам внутри той же сборки (assembly). В C# есть шесть модификаторов доступа:
- public — доступен всем.
- private — доступен только внутри того же типа.
- protected — доступен наследникам.
- internal — доступен внутри той же сборки.
- protected internal — доступен в той же сборке или в классах-наследниках из других сборок.
- private protected — доступен наследникам только в той же сборке.
Важно: используйте наименьший требуемый уровень доступа по принципу наименьших привилегий.
Поля, свойства и доступ к данным
Поля (fields) содержат состояние объекта. Обычно поля делают приватными, а доступ к ним организуют через свойства (properties) или методы доступа (геттеры/сеттеры).
Пример с явными геттерами и сеттерами:
internal class Customer
{
// поля
private int IdNumber;
private string Name;
private double Total;
// сеттеры
public void SetIdNumber(int IdNumber) { this.IdNumber = IdNumber; }
public void SetName(string Name) { this.Name = Name; }
public void SetTotal(double Total) { this.Total = Total; }
// геттеры
public int GetIdNumber() { return this.IdNumber; }
public string GetName() { return this.Name; }
public double GetTotal() { return this.Total; }
}В современных версиях C# предпочтительнее использовать свойства — компактную форму, совмещающую хранение и доступ:
internal class Customer
{
public int IdNumber { get; set; }
public string Name { get; set; }
public double Total { get; set; }
}Свойства позволяют позже подключить логику в get/set, не меняя внешний интерфейс класса.
Советы по оформлению полей и свойств:
- Приватные поля именуйте в camelCase или с префиксом _ (например, _idNumber).
- Публичные свойства используйте в PascalCase (IdNumber, Name).
- Для неизменяемых данных используйте readonly-поля или init-only свойства.
- При работе с nullable-типами явно включайте аннотации nullability и проверяйте входные параметры.
Конструкторы
Конструктор — метод, который вызывается при создании экземпляра класса. Он имеет то же имя, что и класс и не возвращает значение.
Типы конструкторов:
- дефолтный (без параметров) — инициализирует поля значениями по умолчанию;
- параметризованный (первичный) — принимает параметры для установки состояния;
- конструктор копирования — создаёт новый экземпляр на основе существующего.
Дефолтный конструктор
// дефолтный конструктор
public Customer()
{
IdNumber = 0;
Name = "unknown";
Total = 0;
}Параметризованный конструктор
Избегайте параметров, не относящихся к полям. Пример согласованного конструктора:
// параметризованный конструктор
public Customer(int IdNumber, string Name, double Total)
{
this.IdNumber = IdNumber;
this.Name = Name;
this.Total = Total;
}Конструктор копирования
// конструктор копирования
public Customer(Customer previousCustomer)
{
this.IdNumber = previousCustomer.IdNumber;
this.Name = previousCustomer.Name;
this.Total = previousCustomer.Total;
}Конструктор копирования копирует примитивные значения и ссылки. Для глубокого копирования вложенных объектов нужен явный код копирования.
Методы
Метод — это поведение класса. У метода есть модификатор доступа, возвращаемый тип, имя и тело.
// метод
public string CustomerDetail()
{
return "ID: " + IdNumber + " Name: " + Name + " Total: " + Total;
}Это простой пример, возвращающий строковое представление состояния объекта. Для форматирования данных предпочтительнее использовать интерполяцию строк (C#):
public string CustomerDetail()
{
return $"ID: {IdNumber} Name: {Name} Total: {Total}";
}Создание объектов и использование
Создадим объект с дефолтным конструктором:
Customer John = new Customer();
Console.WriteLine(John.Name);Ожидаемый вывод:
unknownСоздание объекта с параметризованным конструктором:
Customer John = new Customer(1001, "John Doe", 250.20);
Console.WriteLine(John.CustomerDetail());Ожидаемый вывод:
ID: 1001 Name: John Doe Total: 250.2Копирование объекта:
Customer Johnny = new Customer(John);
Console.WriteLine(Johnny.CustomerDetail());Вывод:
ID: 1001 Name: John Doe Total: 250.2Если выполнить копирование дефолтного объекта, у копии будут те же значения полей, но это разные ссылки:
Customer John = new Customer();
Customer Johnny = new Customer(John);
Console.WriteLine(Johnny.CustomerDetail());Вывод:
ID: 0 Name: unknown Total: 0Альтернативы классам
Иногда класс — не лучший выбор. Рассмотрите альтернативы:
- struct — value type, полезен для небольших типов, где важна копия значения, а не ссылка. Не используйте struct для больших или мутируемых объектов.
- record — удобен для неизменяемых объектов и поддержки семантики “значения” (value-based equality). Records имеют компактный синтаксис и встроенные методы копирования.
- tuple / anonymous types — для временных групп значений без явного типа.
Пример записи record:
public record CustomerRecord(int IdNumber, string Name, double Total);Records хороши, когда важна семантика неизменяемости и сравнения по значениям.
Когда классы не подходят
- Нужны небольшие, часто копируемые структуры — используйте struct.
- Вам нужна неизменяемая модель данных со встроенным копированием и сравнению по значениям — record предпочтительнее.
- Низкоуровневое управление памятью или производительность в hot path — стоит проводить профилирование и рассматривать value types.
Лучшие практики проектирования классов
- Делайте поля private, предоставляйте публичные свойства.
- Используйте PascalCase для публичных членов и camelCase/_camel для приватных.
- Поддерживайте инварианты в конструкторах: объекты должны быть корректными после создания.
- Предпочитайте небольшие классы с одной ответственностью (Single Responsibility Principle).
- Документируйте публичный API класса комментариями XML.
- Используйте nullability и проверяйте входные параметры.
- Для потокобезопасности делайте объекты неизменяемыми, если возможно.
Important: помните про управление null и проверку границ входных данных в сеттерах и конструкторах.
Паттерны и подходы
- Фабричные методы (Factory) — инкапсулируют сложную логику создания объектов.
- Билдер (Builder) — полезен, когда у класса много параметров.
- DTO — отдельные классы для передачи данных без бизнес-логики.
Пример фабрики:
public static class CustomerFactory
{
public static Customer CreateVip(int id, string name) => new Customer(id, name, 10000);
}Ментальные модели и эвристики
- Класс = шаблон; объект = экземпляр шаблона.
- Поле = состояние; метод = поведение.
- Свойства служат адаптером между внутренним состоянием и внешним интерфейсом.
- Используйте “минимальный публичный интерфейс”: открывайте только то, что требуется.
Решающее дерево: класс, struct или record
flowchart TD
A[Нужен тип данных?] --> B{Будет ли он неизменяемым?}
B -- Да --> C{Нужна ли семантика сравнения по значению?}
B -- Нет --> D{Небольшой ли он и часто копируется?}
C -- Да --> E[Используйте record]
C -- Нет --> F[Используйте class]
D -- Да --> G[Используйте struct]
D -- Нет --> FКонтрольные списки по ролям
Разработчик:
- Поля приватные, свойства публичные.
- Проверки входных параметров в конструкторах.
- Тесты на создание экземпляров и методы.
Архитектор:
- Выбран правильный тип (class/struct/record).
- Слои отделены (DTO vs Domain).
- Миграция/совместимость учтена для публичных API.
Тестировщик:
- Тесты на конструкторы: дефолтный, параметризованный, копирование.
- Тесты на сериализацию/десериализацию, если применимо.
Критерии приёмки
- Экземпляр создаётся без исключений для допустимых входных данных.
- Значения свойств устанавливаются и возвращаются корректно.
- Метод CustomerDetail возвращает ожидаемую строку.
- Копирование через конструктор создаёт отдельный объект со скопированными значениями.
Факты и полезные числа
- Модификаторов доступа в C# — 6.
- Класс — reference type; struct — value type.
- Record поддерживает удобное копирование и сравнение по значениям.
Безопасность и приватность
- Не храните чувствительные данные в публичных свойствах без шифрования.
- Для секретов используйте безопасные хранилища и короткоживущие объекты.
- Учитывайте сериализацию: при сериализации класс может раскрыть внутренние поля.
Краткая методология разработки класса
- Определите ответственность класса и его публичный контракт.
- Выберите подходящий тип (class/struct/record).
- Спроектируйте поля и свойства, обеспечьте инварианты в конструкторе.
- Добавьте методы только для поведения, относящегося к состоянию.
- Напишите модульные тесты и документацию.
Глоссарий (в одну строку)
- Класс — шаблон для создания объектов.
- Объект — экземпляр класса, содержащий состояние и поведение.
- Свойство — интерфейс доступа к полю с возможностью логики доступа.
- Конструктор — метод для инициализации нового экземпляра.
- Метод — операция, выполняющая поведение объекта.
- Экземпляр — конкретный объект в памяти.
Итог
Классы в C# — гибкий инструмент для моделирования сущностей и поведения. Используйте свойства, контролируйте доступ, поддерживайте инварианты в конструкторах и выбирайте тип (class/struct/record) в соответствии с требованиями производительности, семантики сравнения и неизменяемости.
Заметки
- Примеры кода показаны для пояснения; адаптируйте их под конкретную версию C# и стиль проекта.
Summary
- Классы моделируют состояние и поведение в C#.
- Свойства упрощают доступ к полям.
- Конструкторы и паттерны проектирования помогают поддерживать корректность объектов.
- Рассматривайте struct и record как альтернативы в соответствующих сценариях.
Похожие материалы
Как удалить видео в TikTok
Как найти и работать с фантомным писателем
FreeFileSync: синхронизация локальных резервных копий
Как делать скриншоты на Android TV
Как сделать прозрачные окна в Windows 10