В прошлом уроке мы говорили о том, что чистый код — это код, который легко читается и поддерживается. Звучит красиво, но с чего конкретно начать? Ответ проще, чем кажется: с имён.

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

public class pc
{
    public string N;
    public int hp;
    public int x;
    public int y;

    public void Do()
    {
        hp = hp - 10;
        if (hp < 0) hp = 0;
    }

    public bool Check()
    {
        return hp > 0;
    }
}

Что такое pc? Player Character? Personal Computer? Что за N? Имя? Никнейм? Номер? Что делает Do()? Что именно проверяет Check()? У вас десятки вопросов — и ни одного ответа в коде.

Вы оборачиваетесь к коллеге за соседним столом. Это Фёдор — он здесь уже третий год.

Фёдор: А, это классика. Предыдущий разработчик писал так, будто за каждый символ платил из зарплаты. Мы полдня потратили, пока разобрались, что Do() — это получение урона, а Check() — проверка, жив ли персонаж. Именно поэтому существуют соглашения об именовании.

И Фёдор прав. Соглашения об именовании — это как дорожные знаки: если все следуют одним правилам, никто не заблудится. Код читается как текст, а не как шифровка. Давайте разберёмся, какие «знаки» приняты в C#. Начнём с самого фундаментального — стилей записи имён. В C# их два основных.

Название пришло из языка Pascal, где этот стиль был стандартом. C# унаследовал его и сделал основным для большинства элементов кода.

Почему «верблюжий»? Посмотрите на слово playerScore — заглавные буквы посередине торчат вверх, как горбы верблюда. Отсюда и название.

Правило простое: PascalCase — для «больших» вещей (классы, методы, свойства), camelCase — для «маленьких» (локальные переменные, параметры, приватные поля). Скоро мы разберём это подробнее.

Есть ещё несколько стилей, которые вам могут встретиться, хотя в современном C# они почти не используются:

snake_case — слова разделяются подчёркиваниями: player_score, max_value. Любимый стиль Python и SQL, но в C# он не прижился.

SCREAMING_SNAKE_CASE — тот же snake_case, но капсом: MAX_SPEED, DEFAULT_SIZE. Часто используется для констант в C/C++. В C# Microsoft рекомендует писать константы в PascalCase, но некоторые команды по привычке используют «кричащий» стиль.

Венгерская нотация — к имени добавляется префикс с типом: strName, iCount, btnStart. Когда-то это было необходимо, потому что IDE не подсказывали типы. Сегодня это рудимент — в C# язык строго типизирован, а IDE показывает всё сама.

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

Помните класс pc, от которого вы пришли в ужас? Фёдор говорит:

Фёдор: Смотри, принцип простой. Всё, что является «большой сущностью» — классы, интерфейсы, методы, свойства, события, перечисления — пишется в PascalCase. Всё, что «внутренняя мелочь» — локальные переменные, параметры, приватные поля — в camelCase. Есть пара специальных правил, но их немного.

Давайте посмотрим, как выглядит класс, написанный по всем соглашениям. Вот тот самый pc, только по-человечески:

public class PlayerCharacter                    // Класс — PascalCase
{
    private string _name;                        // Приватное поле — _camelCase
    private int _health;

    public string Name { get; set; }             // Свойство — PascalCase
    public int Health { get; private set; }
    public bool IsAlive => Health > 0;           // Свойство — PascalCase

    public event Action<int> DamageTaken;        // Событие — PascalCase

    public void TakeDamage(int damageAmount)     // Метод — PascalCase, параметр — camelCase
    {
        int newHealth = Health - damageAmount;   // Локальная переменная — camelCase
        Health = newHealth < 0 ? 0 : newHealth;
        DamageTaken?.Invoke(damageAmount);
    }
}

Видите, как всё сразу встало на свои места? Каждое имя говорит само за себя: PlayerCharacter — не загадочный pc, TakeDamage — не таинственный Do(), IsAlive — не непонятный Check().

Теперь — специальные правила. Их всего два:

Интерфейсы всегда начинаются с буквы I:

public interface IMovable         // I + PascalCase
{
    void Move(float deltaTime);
}

public interface IDamageable
{
    void TakeDamage(int amount);
}

Обобщённые параметры (Generics) начинаются с буквы T:

public class Repository<TEntity>  // T + PascalCase
{
    public TEntity Find(int id) { ... }
}

public interface IFactory<TProduct>
{
    TProduct Create();
}

Для наглядности — сводная таблица всех соглашений:

| Элемент | Стиль | Пример | | --- | --- | --- | | Пространство имён | PascalCase | GameStudio.Engine.Physics | | Класс / Структура | PascalCase | PlayerCharacter | | Интерфейс | I + PascalCase | IMovable | | Метод | PascalCase | TakeDamage() | | Свойство | PascalCase | Health, IsAlive | | Событие | PascalCase | DamageTaken | | Делегат | PascalCase | DamageHandler | | Перечисление (enum) | PascalCase | GameState.Playing | | Обобщённый параметр | T + PascalCase | TEntity, TProduct | | Публичное поле | PascalCase | Score | | Приватное поле | _camelCase | _health | | Локальная переменная | camelCase | newHealth | | Параметр метода | camelCase | damageAmount | Со стилями разобрались. Но правильный регистр — это только половина дела. Имя может быть в идеальном PascalCase и при этом оставаться бесполезным. Фёдор заглядывает в ваш первый коммит и начинает код-ревью:

Фёдор: Слушай, у тебя тут метод Process(). Что именно он «процессит»? Загружает уровень? Обрабатывает урон? Сохраняет данные? Имя должно отвечать на вопрос что именно делает код.

И Фёдор прав. Вот главные принципы хороших имён:

Имя должно раскрывать намерение. Хорошее имя отвечает на вопрос «Что это?» или «Что делает?» без заглядывания в реализацию.

// Плохо — приходится гадать
int value = 10;
void Process() { }

// Хорошо — всё понятно с первого взгляда
int maxHealth = 100;
void LoadLevel(string levelName) { }

Не сокращайте — если только это не общепринятая аббревиатура вроде HTTP, UI, API.

// Плохо — экономия на спичках
int plrSpd = 5;
string crtPlr = "Alex";

// Хорошо — читается как текст
int playerSpeed = 5;
string currentPlayer = "Alex";

Если сомневаетесь, сокращать или нет, — не сокращайте. Длинное понятное имя всегда лучше короткого непонятного.

Избегайте двусмысленности. Имя не должно допускать два толкования.

// Плохо — что конкретно значит "обработать"?
void ProcessPlayer(Player player) { }

// Хорошо — сразу ясно
void ValidatePlayer(Player player) { }
void SavePlayer(Player player) { }

Не злоупотребляйте словами-пустышками. Слова Manager, Data, Info, Handler кажутся универсальными, но часто ничего не добавляют к смыслу.

// Плохо — три слова, ноль информации
public class PlayerInfoDataManager { }

// Хорошо — конкретно и лаконично
public class PlayerStats { }

Фёдор: И ещё — никаких магических чисел. Если я вижу speed2 или amountX, я не знаю, что за «2» и что за «X». Назови bonusSpeed и damageMultiplier — и вопросов не будет.

// Плохо — магические имена
int speed2 = 10;
int amountX = 3;

// Хорошо — имя объясняет роль
int bonusSpeed = 10;
int damageMultiplier = 3;

Аббревиатуры. Если аббревиатура длиннее двух букв, в PascalCase только первая буква заглавная: HttpClient, JsonParser, ApiService. Если аббревиатура из двух букв, обе остаются заглавными: UIManager, IOStream. Помните тот ужасный класс pc, с которого мы начали? Давайте посмотрим на него ещё раз — и рядом положим отрефакторенную версию:

// БЫЛО — код-загадка
public class pc
{
    public string N;
    public int hp;
    public int x;
    public int y;

    public void Do()
    {
        hp = hp - 10;
        if (hp < 0) hp = 0;
    }

    public bool Check()
    {
        return hp > 0;
    }
}
// СТАЛО — код, который говорит сам за себя
public class PlayerCharacter
{
    public string Name { get; set; }
    public int Health { get; private set; }
    public int PositionX { get; set; }
    public int PositionY { get; set; }

    public bool IsAlive => Health > 0;

    public void TakeDamage(int damageAmount)
    {
        Health -= damageAmount;
        if (Health < 0) Health = 0;
    }
}

Ни одного комментария — и всё абсолютно понятно. TakeDamage — получает урон. IsAlive — жив ли персонаж. Health — здоровье. Код читается как текст. Это и есть сила хороших имён.

Ссылки на официальную документацию Microsoft:

  • .NET Naming Guidelines — главный документ с правилами именования в .NET.
  • C# Coding Conventions — практичный набор соглашений по оформлению кода.

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

camelCase — первое слово с маленькой буквы, остальные с заглавной; используется для локальных переменных, параметров и приватных полей.

Приватные поля — именуются с подчёркиванием в начале: _health.

Интерфейсы — всегда начинаются с буквы I: IMovable, IDamageable.

Обобщённые параметры — начинаются с буквы T: TEntity, TProduct.

Хорошее имя — раскрывает намерение, не содержит сокращений и не допускает двусмысленности.

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

Обсуждение урока

0
Комментарии видны всем. Чтобы участвовать в обсуждении, войдите или зарегистрируйтесь.
Модерация сообщества

Пожаловаться на комментарий

Расскажите модераторам, что именно требует внимания.