Clean Code на C#: принципы и примеры для .NET разработчиков

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Clean Code на C#: принципы и примеры на практике

Чистый код (Clean Code) — это не просто модный термин, а фундаментальный навык профессионального разработчика. В экосистеме .NET и C#, где проекты часто живут годами и поддерживаются десятками программистов, умение писать понятный и легко изменяемый код становится критически важным. В этой статье мы разберем основные принципы Clean Code применительно к C# и рассмотрим конкретные примеры их применения.

Почему чистый код важен в C#?

C# — строго типизированный, объектно-ориентированный язык с богатой экосистемой. Однако его мощь может обернуться хаосом, если не следовать дисциплине. Чистый код уменьшает количество багов, ускоряет онбординг новых разработчиков и снижает стоимость поддержки. Как говорил Роберт Мартин: «Единственный способ быстро писать код — это писать его хорошо».

Принцип 1: Говорящие имена

Имена переменных, методов и классов должны раскрывать намерение. Не экономьте символы — плохой код требует больше времени на понимание.

Плохой пример

int d; // время с момента изменения
List<int> lst = new List<int>();
void Proc(int a, int b) { ... }

Хороший пример (Clean Code)

int elapsedTimeInDays;
List<int> customerIds = new List<int>();
void CalculateDiscount(decimal basePrice, decimal taxRate) { ... }

Правило: Если имя требует комментария, значит оно плохое. Используйте camelCase для переменных и PascalCase для методов и классов.

Принцип 2: Маленькие функции (SRP на уровне методов)

Каждая функция должна делать ровно одну вещь и делать её хорошо. Если метод содержит более 20-30 строк или имеет вложенность более двух уровней — это сигнал к рефакторингу.

Плохой пример: функция-швейцарский нож

public void ProcessOrder(Order order)
{
    // Валидация
    if (order == null || order.Items.Count == 0)
        throw new ArgumentException("Invalid order");

    // Расчет скидки
    decimal discount = 0;
    if (order.Total > 1000) discount = 0.1m;
    else if (order.Total > 500) discount = 0.05m;

    // Сохранение в БД
    using var db = new AppDbContext();
    db.Orders.Add(order);
    db.SaveChanges();

    // Отправка email
    SendEmail(order.CustomerEmail, "Order received");
}

Хороший пример: разделение ответственности

public void ProcessOrder(Order order)
{
    Validate(order);
    var discount = CalculateDiscount(order.Total);
    ApplyDiscount(order, discount);
    SaveOrder(order);
    SendConfirmationEmail(order);
}

private void Validate(Order order)
{
    if (order == null || order.Items.Count == 0)
        throw new ArgumentException("Invalid order");
}

private decimal CalculateDiscount(decimal total)
{
    if (total > 1000) return 0.1m;
    if (total > 500) return 0.05m;
    return 0;
}

private void SaveOrder(Order order)
{
    using var db = new AppDbContext();
    db.Orders.Add(order);
    db.SaveChanges();
}

Принцип 3: Избегайте дублирования (DRY)

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

Пример: дублирование логики

// В контроллере пользователей
if (user.Role == "Admin" || user.Role == "Manager")
    GrantAccess();

// В контроллере отчетов
if (user.Role == "Admin" || user.Role == "Manager")
    ShowReport();

Рефакторинг: инкапсуляция проверки

public static class UserExtensions
{
    public static bool HasElevatedPermissions(this User user)
    {
        return user.Role == "Admin" || user.Role == "Manager";
    }
}

// Использование
if (user.HasElevatedPermissions())
    GrantAccess();

Принцип 4: Комментарии — зло (кроме исключений)

Комментарии часто врут и устаревают. Лучший комментарий — хорошо написанный код. Используйте комментарии только для объяснения почему вы сделали что-то неочевидное, а не что делает код.

Плохой комментарий

// Увеличиваем счетчик на 1
counter++;

Хороший комментарий (объяснение причины)

// Используем int вместо decimal для производительности,
// так как точность до 2 знаков не требуется
int priceInCents = (int)(price * 100);

Принцип 5: SOLID для архитектуры C#

Принципы SOLID — основа чистого кода в объектно-ориентированных языках. Рассмотрим главный из них — Принцип открытости/закрытости (OCP).

Нарушение OCP: класс, требующий изменений

public class DiscountCalculator
{
    public decimal Calculate(Order order, string customerType)
    {
        if (customerType == "Regular") return order.Total * 0.05m;
        if (customerType == "VIP") return order.Total * 0.15m;
        if (customerType == "Employee") return order.Total * 0.25m;
        return 0;
    }
}

Решение: стратегия (Strategy Pattern)

public interface IDiscountStrategy
{
    decimal Calculate(Order order);
}

public class VipDiscount : IDiscountStrategy
{
    public decimal Calculate(Order order) => order.Total * 0.15m;
}

public class DiscountCalculator
{
    private readonly IDiscountStrategy _strategy;

    public DiscountCalculator(IDiscountStrategy strategy)
    {
        _strategy = strategy;
    }

    public decimal Calculate(Order order) => _strategy.Calculate(order);
}

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

Практические рекомендации для .NET разработчиков

  • Используйте LINQ осмысленно: цепочки из 10 методов LINQ — плохая практика. Разбивайте на промежуточные переменные с понятными именами.
  • Избегайте магических чисел: выносите константы в private const или отдельные классы конфигурации.
  • Применяйте null-безопас

Блоги

Рекомендаций по книгам