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

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

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

Начать курс

Clean Code на C#: как писать код, который хочется читать

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

Почему Clean Code важен для C# разработчика?

Чистый код — это не просто красивое форматирование. Это набор практик, которые делают код:

  • Читаемым — любой разработчик быстро поймёт логику;
  • Поддерживаемым — легко вносить изменения без риска сломать систему;
  • Тестируемым — код легко покрывается юнит-тестами;
  • Эффективным — отсутствие лишних абстракций и дублирования.

В C# с его строгой типизацией и мощными возможностями .NET (LINQ, async/await, Records) следование принципам Clean Code особенно важно. Плохой код на C# может привести к неочевидным багам и проблемам с производительностью.

Принцип 1: Именование переменных и методов

Имя переменной или метода должно отвечать на вопрос "что это?" или "что делает?". Избегайте однобуквенных сокращений (кроме индексов циклов).

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

public class Data
{
    public int a; // что это?
    public string b; // непонятно

    public void Do(int x, int y) // что делает метод?
    {
        var r = x + y;
        Console.WriteLine(r);
    }
}

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

public class Customer
{
    public int Id { get; set; }
    public string FullName { get; set; }

    public void PrintCustomerSummary(int customerId, int orderCount)
    {
        var totalOrders = customerId + orderCount;
        Console.WriteLine($"Customer {customerId} has {totalOrders} orders");
    }
}

Правило: Имена должны быть выразительными. Если переменная называется data, это почти всегда плохо. Используйте customerData, orderList, invoiceTotal.

Принцип 2: Маленькие методы с одной ответственностью

Каждый метод должен делать ровно одну вещь. Если метод содержит более 10-15 строк, скорее всего, его нужно разбить. Это основа принципа Single Responsibility (из SOLID).

Плохой пример (метод делает всё)

public void ProcessOrder(int orderId)
{
    // 1. Валидация
    if (orderId <= 0) throw new ArgumentException("Invalid ID");
    
    // 2. Получение данных из БД
    var order = _db.Orders.Find(orderId);
    
    // 3. Рассчёт скидки
    decimal discount = 0;
    if (order.Total > 1000) discount = order.Total * 0.1m;
    
    // 4. Отправка email
    var email = new MailMessage();
    email.To.Add(order.CustomerEmail);
    email.Subject = "Order processed";
    
    // 5. Логирование
    _logger.LogInformation("Order {Id} processed", orderId);
}

Хороший пример (рефакторинг)

public void ProcessOrder(int orderId)
{
    ValidateOrderId(orderId);
    var order = GetOrderFromDatabase(orderId);
    var discount = CalculateDiscount(order);
    SendConfirmationEmail(order, discount);
    LogOrderProcessing(orderId);
}

private void ValidateOrderId(int orderId)
{
    if (orderId <= 0)
        throw new ArgumentException("Order ID must be positive", nameof(orderId));
}

private Order GetOrderFromDatabase(int orderId)
{
    return _db.Orders.Find(orderId) 
           ?? throw new InvalidOperationException("Order not found");
}

private decimal CalculateDiscount(Order order)
{
    return order.Total > 1000 ? order.Total * 0.1m : 0;
}

private void SendConfirmationEmail(Order order, decimal discount)
{
    var email = new MailMessage
    {
        To = { order.CustomerEmail },
        Subject = "Order processed",
        Body = $"Your total with discount: {order.Total - discount:C}"
    };
    _emailService.Send(email);
}

private void LogOrderProcessing(int orderId)
{
    _logger.LogInformation("Order {Id} processed successfully", orderId);
}

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

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

Дублирование кода — главный враг поддерживаемости. Если вы копируете блок кода более одного раза, выносите его в метод или класс.

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

// В методе A
var totalA = itemsA.Sum(i => i.Price);
var taxA = totalA * 0.2m;
var finalA = totalA + taxA;

// В методе B (дублирование)
var totalB = itemsB.Sum(i => i.Price);
var taxB = totalB * 0.2m;
var finalB = totalB + taxB;

Хороший пример

public decimal CalculateFinalTotal(IEnumerable<Item> items, decimal taxRate = 0.2m)
{
    var subtotal = items.Sum(i => i.Price);
    var tax = subtotal * taxRate;
    return subtotal + tax;
}

// Использование
var finalA = CalculateFinalTotal(itemsA);
var finalB = CalculateFinalTotal(itemsB);

Совет: В C# активно используйте LINQ, extension methods и generic-методы для устранения дублирования.

Принцип 4: Управление сложностью и форматирование

Чистый код подразумевает не только логику, но и визуальную структуру. Правильное форматирование и использование современных возможностей C# делают код понятнее.

Используйте выражения switch (Pattern Matching)

// Вместо длинных if-else
public string GetDiscountCategory(decimal total) => total switch
{
    < 100 => "No discount",
    < 500 => "Silver",
    < 1000 => "Gold",
    _ => "Platinum"
};

Используйте Records для DTO

// Вместо классов с кучей кода
public record CustomerDto(int Id, string Name, string Email);

// Использование
var customer = new CustomerDto(1, "John Doe", "john@example.com");

Правильное форматирование

// Плохо: всё в одну строку
if (user.IsActive) {                

Блоги

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