Avatar ·

Порядок элементов в классе C#: поля, свойства, конструкторы, методы — есть ли стандарт?

Всем привет! Я начинающий C# разработчик, и меня мучает один вопрос. Когда я пишу классы, я просто добавляю поля, потом свойства, потом конструкторы и методы в том порядке, в котором они приходят мне в голову. Но недавно на code-review старший разработчик сделал замечание: "Неправильный порядок элементов в классе".

Я полез в интернет и нашел много разных мнений. Кто-то пишет: сначала все private поля, потом public свойства, потом конструкторы, потом методы. Другие говорят: сначала public члены, потом protected, потом private. Третьи вообще сортируют по алфавиту.

Вот пример моего класса, который раскритиковали:

public class Employee
{
    public string LastName { get; set; }
    private int _age;
    public Employee(string name) { Name = name; }
    public string Name { get; set; }
    public void PrintInfo() { Console.WriteLine($"{Name}, {_age}"); }
    private int _id;
    public int Age { get { return _age; } set { _age = value; } }
    public Employee(string name, int age) : this(name) { _age = age; }
    private void ValidateAge() { if (_age < 0) throw new Exception(); }
    public int Id { get { return _id; } }
}

Подскажите, пожалуйста, есть ли официальный стандарт или общепринятое соглашение по порядку членов класса в C#? И как правильно организовать код в моем примере? Заранее спасибо!

<<>>

Официальные рекомендации и общепринятые соглашения

В C# нет жесткого стандарта, навязываемого компилятором, но есть два мощных источника: StyleCop (инструмент анализа кода от Microsoft) и соглашения команды .NET Runtime. Они задают де-факто стандарт, который используют 90% профессиональных C# разработчиков.

Основной порядок (по убыванию важности)

  1. Константы и поля только для чтения (const, readonly)
  2. Статические поля
  3. Статический конструктор
  4. Обычные поля (private)
  5. Свойства (сначала авто-свойства, потом полные)
  6. Конструкторы (от самого простого к сложному)
  7. Методы (сначала public, потом private)
  8. Вложенные типы (классы, структуры, перечисления)

Исправленный пример вашего класса

public class Employee
{
    // 1. Поля (private, сначала статические, потом обычные)
    private static int _nextId = 1;
    private readonly int _id;
    private int _age;

    // 2. Свойства (public, затем private)
    public string Name { get; set; }
    public string LastName { get; set; }
    public int Age
    {
        get { return _age; }
        set 
        { 
            ValidateAge(value);
            _age = value; 
        }
    }
    public int Id => _id;  // Expression-bodied property

    // 3. Конструкторы (от простого к сложному)
    public Employee(string name) : this(name, 0) { }
    
    public Employee(string name, int age)
    {
        Name = name ?? throw new ArgumentNullException(nameof(name));
        _id = _nextId++;
        Age = age;
    }

    // 4. Публичные методы
    public void PrintInfo()
    {
        Console.WriteLine($"ID: {Id}, Name: {Name} {LastName}, Age: {Age}");
    }

    // 5. Приватные методы (вспомогательные)
    private void ValidateAge(int age)
    {
        if (age < 0 || age > 150)
            throw new ArgumentOutOfRangeException(nameof(age), "Age must be between 0 and 150");
    }
}

Дополнительные важные правила

  • Группировка по доступу: внутри каждой секции сначала public, потом internal, потом protected, потом private.
  • Связанные члены рядом: если свойство использует поле, они должны быть рядом (поле сразу над свойством).
  • Регионы (regions): не злоупотребляйте ими. Лучше разбивайте класс на несколько partial-классов, если он слишком большой.
  • Интерфейсы: явные реализации интерфейсов обычно размещают в самом конце класса.

Почему это важно?

Упорядоченный код:

  • Облегчает навигацию по классу (вы знаете, где искать)
  • Упрощает code-review (коллеги быстрее понимают структуру)
  • Соответствует ожиданиям большинства C# разработчиков
  • Помогает избежать дублирования и ошибок

Для автоматизации используйте StyleCop.Analyzers (NuGet пакет) — он будет подсвечивать нарушения прямо в Visual Studio. Или просто нажмите Ctrl+R, Ctrl+G в VS для автоматической сортировки членов класса.

<<>>

Альтернативный подход: группировка по функциональности

Некоторые команды предпочитают не жесткий порядок "поля-свойства-методы", а группировку по логическим блокам. Особенно это актуально для больших классов (более 100 строк).

Пример:

public class Employee
{
    // === Блок: Идентификация ===
    public int Id { get; }
    public string Name { get; set; }
    
    // === Блок: Возраст и валидация ===
    private int _age;
    public int Age 
    { 
        get => _age; 
        set => _age = ValidateAge(value); 
    }
    private int ValidateAge(int age) 
    { 
        return age >= 0 && age <= 150 ? age : throw new ArgumentException(); 
    }
    
    // === Блок: Конструкторы ===
    public Employee(string name) => Name = name;
    public Employee(string name, int age) : this(name) => Age = age;
    
    // === Блок: Поведение ===
    public void PrintInfo() => Console.WriteLine($"{Id}: {Name}, {Age}");
}

Этот подход нарушает строгий порядок StyleCop, но делает код более читаемым, когда поля и методы логически связаны. Главное — договориться о едином стиле в команде и зафиксировать его в .editorconfig. Оба подхода имеют право на жизнь, но первый (StyleCop) более универсален и подходит для любых проектов.