C# в команде: code style и конвенции, которые спасут ваш проект

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

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

Начать курс

Почему code style — это не про «я так вижу»

Вы когда-нибудь открывали чужой код на C# и чувствовали себя археологом, раскапывающим руины древнего храма? public int a;, var x = GetSmth() без контекста и методы длиной в 400 строк. Если да — вы знаете, как болит голова команды без единого code style.

Работа в команде — это не только про Git и code review. Это про то, что ваш код читают другие люди. И если вы пишете для себя, то code style для C# — это скучная бюрократия. Но если вы пишете для команды — это спасение нервов, времени и продакшена.

В этой статье мы разберём, какие конвенции C# реально работают в 2024 году, как внедрить их без боли и почему EditorConfig — ваш лучший друг. Поехали.

Базовые правила: о чём договариваться в первую очередь

1. Именование (naming conventions)

В C# есть жёсткие стандарты, но команды часто их нарушают. Вот золотой минимум, который принимает 90% .NET-команд:

  • PascalCase — для классов, методов, свойств, публичных полей: public class UserService, public string GetUserName().
  • camelCase — для локальных переменных, параметров методов, приватных полей (часто с префиксом _): private int _userId;, var userName = "Alex";.
  • Избегайте венгерской нотации — strName, intCount — это прошлый век. Тип переменной должен быть очевиден из контекста.
  • Не сокращайте без необходимости — GetUsrInf() хуже, чем GetUserInformation(). Исключение: общепринятые аббревиатуры (IO, UI, DTO).
// Плохо
public class user_service
{
    public int id;
    public string nm;
    private string _data;
    public string GetData() => _data;
}

// Хорошо
public class UserService
{
    public int Id { get; }
    public string Name { get; }
    private readonly string _userData;

    public string GetUserData() => _userData;
}

2. Форматирование: пробелы, скобки, пустые строки

Споры о том, ставить ли пробел перед двоеточием в catch (Exception e) — бесконечны. Поэтому используйте автоматические инструменты. Но базовые договорённости всё же нужны:

  • Отступы — 4 пробела (табуляцию в C# используют редко, но если команда выбрала табы — это нормально).
  • Фигурные скобки — всегда на новой строке (Allman style). Это стандарт для C#.
  • Одна пустая строка между методами. Две — между классами.
// Плохо (скобки на той же строке — это Java, не C#)
public void Process() {
    var result = Calculate();
    return result;
}

// Хорошо (Allman style)
public void Process()
{
    var result = Calculate();
    return result;
}

Продвинутые конвенции: на чём чаще всего спотыкаются

3. Использование var

Одна из вечных дискуссий. Правило простое: используйте var, когда тип очевиден из правой части. Если нет — пишите явно.

// Можно var
var users = GetUsers(); // тип очевиден
var dictionary = new Dictionary<string, int>(); // длинное объявление

// Лучше явно
User user = GetUserById(id); // неочевидно, что возвращается User
Dictionary<string, int> result = ParseData(); // тоже неочевидно

Если ваша команда не любит var — просто договоритесь. Главное — единообразие.

4. Работа с LINQ

LINQ — мощный, но коварный инструмент. Частая ошибка — цепочки длиной в 10 методов, которые читаются как заклинание. Договоритесь:

  • Разбивайте длинные LINQ-выражения на несколько строк.
  • Не злоупотребляйте .Select() там, где нужен простой foreach.
  • Избегайте побочных эффектов внутри LINQ (например, вызова Console.WriteLine() внутри .ForEach() — это антипаттерн).
// Плохо: всё в одну строку
var result = data.Where(x => x.IsActive).Select(x => new { x.Name, x.Age }).OrderBy(x => x.Age).ToList();

// Хорошо: читаемо
var activeUsers = data.Where(x => x.IsActive);
var userInfos = activeUsers.Select(x => new { x.Name, x.Age });
var sorted = userInfos.OrderBy(x => x.Age).ToList();

5. Обработка ошибок

Типичная проблема — пустые catch блоки. Если вы ловите исключение — либо логируйте, либо пробрасывайте дальше. Не глотайте ошибки молча.

// Плохо
try
{
    ProcessData();
}
catch (Exception)
{
    // ничего не делаем
}

// Хорошо
try
{
    ProcessData();
}
catch (Exception ex)
{
    Logger.LogError(ex, "Ошибка при обработке данных");
    throw; // или throw new CustomException("...", ex);
}

Инструменты: как заставить команду соблюдать code style

Договорённости — это хорошо, но люди забывают. Поэтому нужны автоматические инструменты.

EditorConfig

Файл .editorconfig в корне проекта — это база. Он подхватывается Visual Studio, Rider и VS Code. Пример минимального набора:

root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.cs]
csharp_new_line_before_open_brace = all
csharp_prefer_braces = true
csharp_style_var_for_built_in_types = true
csharp_style_var_when_type_is_apparent = true

.NET Analyzers и StyleCop

Включите в проект NuGet-пакет StyleCop.Analyzers или используйте встроенные анализаторы Roslyn. Они подсветят нарушения прямо в редакторе.

<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>

Настройте правила в .editorconfig или в отдельном файле stylecop.json. Например, можно отключить обязательные XML-комментарии для приватных методов — они не всегда нужны.

Code review checklist

Создайте чек-лист для пул-реквестов. Вот минимальный набор

Блоги

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