| 1 | # Принципы написания кода (C# / .NET) v1 |
| 2 | |
| 3 | Публичный канон для **kb-public** и роутера: нормы стиля, асинхронности и работы в репозитории. **Расширенная версия, датированные постмортемы и черновые заметки** — в `knowledge/work/coding/code-writing.md` (в выгрузке без `work/` недоступен). |
| 4 | |
| 5 | Этот файл **не заменяет** официальную документацию Microsoft: [Framework Design Guidelines](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/), [C# coding conventions](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions), [identifier names](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/identifier-names). |
| 6 | |
| 7 | --- |
| 8 | |
| 9 | ## Опорные внешние ссылки |
| 10 | |
| 11 | | Тема | Ссылка | |
| 12 | |------|--------| |
| 13 | | Соглашения по C# (docs) | [Common C# code conventions](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions) | |
| 14 | | Имена | [Identifier names](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/identifier-names) | |
| 15 | | Стиль .NET Runtime | [coding-style.md](https://github.com/dotnet/runtime/blob/main/docs/coding-guidelines/coding-style.md) | |
| 16 | | Публичные API | [Framework Design Guidelines](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/) | |
| 17 | | `ConfigureAwait` | [ConfigureAwait FAQ](https://devblogs.microsoft.com/dotnet/configureawait-faq/) | |
| 18 | | `IDisposable` / `using` | [using statement](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement), [`IAsyncDisposable`](https://learn.microsoft.com/en-us/dotnet/api/system.iasyncdisposable) | |
| 19 | | `HttpClient` | [HttpClientFactory / устойчивые HTTP-вызовы](https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests) | |
| 20 | | Культура, форматы | [`CultureInfo`](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo) (`InvariantCulture` vs `CurrentCulture`) | |
| 21 | | Логирование | [Logging in .NET](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging) | |
| 22 | | Уязвимости NuGet | [`dotnet list package`](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-list-package) (в т.ч. `--vulnerable`) | |
| 23 | |
| 24 | Инструменты: [.editorconfig](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-style-rule-options), `dotnet format`, анализаторы (`Microsoft.CodeAnalysis.NetAnalyzers` и правила проекта). |
| 25 | |
| 26 | --- |
| 27 | |
| 28 | ## Именование (кратко) |
| 29 | |
| 30 | - Типы, методы, свойства, события — `PascalCase`; параметры и локальные — `camelCase`. |
| 31 | - Интерфейсы — префикс `I`; атрибуты — суффикс `Attribute`. |
| 32 | - Приватные поля — часто `_camelCase`; статические приватные — `s_`; thread-static — `t_` (см. runtime guide). |
| 33 | - Не использовать `__` подряд в именах (зарезервировано компилятором). |
| 34 | - Ясность важнее краткости; аббревиатуры — только общепринятые. |
| 35 | |
| 36 | --- |
| 37 | |
| 38 | ## Вёрстка и формат |
| 39 | |
| 40 | - Скобки Allman; отступ как в проекте; явная видимость у членов типа. |
| 41 | - Согласованность с файлом/компонентом и `.editorconfig`; новый код не ломает стиль соседнего кода без причины. |
| 42 | |
| 43 | --- |
| 44 | |
| 45 | ## Язык и надёжность |
| 46 | |
| 47 | - Возможности актуального `LangVersion`; nullable reference types — осмысленные контракты, без злоупотребления `!`. |
| 48 | - `var`, когда тип очевиден; иначе явный тип. |
| 49 | - Ключевые слова типов (`string`, `int`), не `System.String` в обычном коде. |
| 50 | - Исключения — ловить только обрабатываемое; не использовать исключения для обычного control flow. |
| 51 | - LINQ — для читаемости; на hot path — измерять. |
| 52 | |
| 53 | --- |
| 54 | |
| 55 | ## Инженерные принципы (кратко) |
| 56 | |
| 57 | - **Магические числа и строки** — по возможности заменять **именованными константами/enum**, имя отражает смысл; «голое» число/строка допустимы, если контекст однозначен — иначе это сознательное исключение. |
| 58 | - **SOLID, DRY, KISS** — ориентиры, не догма; отступление нормально, если **осознанно** (зачем и какой ценой). |
| 59 | - **Композиция важнее наследования**; глубокие иерархии и «базовый класс на всё» — только при явной пользе. См. [Framework Design Guidelines](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/) (в т.ч. устойчивые абстракции, члены типов). |
| 60 | - **Разделение ответственности**: узкие типы и методы; зависимости — через абстракции/конструктор там, где это упрощает тесты и смену реализаций. |
| 61 | |
| 62 | --- |
| 63 | |
| 64 | ## Асинхронность |
| 65 | |
| 66 | - `async`/`await` для I/O; избегать sync-over-async (`.Result`, `.Wait()`, `GetAwaiter().GetResult()`) без веской причины и комментария. |
| 67 | - `async void` — только обработчики событий UI; иначе `Task` / `ValueTask`. |
| 68 | - **`ConfigureAwait(false)`** — в обобщённом **библиотечном** коде, на каждом релевантном `await` (см. FAQ). В коде приложения (UI после await, типичный ASP.NET Core) — не навязывать `false` без нужды. |
| 69 | - `CancellationToken` по цепочке для отменяемых операций. |
| 70 | |
| 71 | --- |
| 72 | |
| 73 | ## Тесты и качество |
| 74 | |
| 75 | - TDD или тест рядом с изменением — предпочтительно для нетривиальной логики; осознанные исключения допустимы. |
| 76 | - Рефакторинг без смены поведения — те же проверки/тесты; для UI — снапшоты или критичные сценарии. |
| 77 | - Предупреждения анализаторов: сначала причина, потом фикс; подавления — с обоснованием. |
| 78 | |
| 79 | --- |
| 80 | |
| 81 | ## Безопасность |
| 82 | |
| 83 | - Секреты не в коде; ввод извне — валидация и безопасные паттерны (параметризация, экранирование) по доменным playbook. |
| 84 | |
| 85 | --- |
| 86 | |
| 87 | ## Типичные ловушки (кратко) |
| 88 | |
| 89 | - **Ресурсы:** освобождать `IDisposable` / `IAsyncDisposable` через `using` / `await using`; не подавлять исключения так, что объект остаётся в невалидном состоянии без явной схемы. |
| 90 | - **`HttpClient`:** не создавать новый экземпляр на каждый запрос; в ASP.NET — **`IHttpClientFactory`** / политика переиспользования; иначе — один долгоживущий экземпляр по документации для сценария. |
| 91 | - **`IEnumerable`:** повторное перечисление может быть дорогим или с побочными эффектами — для нескольких проходов **материализовать** (`ToList` и т.п.) осознанно. |
| 92 | - **Культура:** для протоколов, парсинга «сырых» данных, логов в машиночитаемом виде — **`InvariantCulture`**; для текста UI — **`CurrentCulture`** (см. таблицу ссылок). |
| 93 | - **Логи:** структурированное логирование и уровни; **не** писать секреты и лишний PII. |
| 94 | - **Потоки:** избегать общей **изменяемой статики** без синхронизации; при конкуренции — `Concurrent*`, явные примитивы (`lock`, `SemaphoreSlim`) по смыслу. |
| 95 | - **`ValueTask`:** использовать только понимая семантику (в т.ч. повторный `await` одного и того же экземпляра); иначе предпочтительнее `Task`. |
| 96 | - **Сборка / AOT:** при таргете **Native AOT** / trimming — отдельные ограничения (рефлексия, динамика); см. [Native AOT](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/). |
| 97 | - **Зависимости:** периодически **`dotnet list package --vulnerable`** и обновления с учётом breaking changes. |
| 98 | |
| 99 | --- |
| 100 | |
| 101 | ## Операционные правила репозитория |
| 102 | |
| 103 | - Коммиты **по смыслу** (фича / рефакторинг / стиль — раздельно). |
| 104 | - Массовые правки — **узкая область**; `externals/**` и вендор не трогать без решения. |
| 105 | - Рефакторинг после проверки; имена констант и ID — **доменный смысл**. |
| 106 | - Сложность: один смысл на метод; рост файла — сигнал к разбиению. |
| 107 | |
| 108 | --- |
| 109 | |
| 110 | ## Версионирование |
| 111 | |
| 112 | - **v1** — публичный срез для kb-public; актуальная дата смыслового обновления — **2026-04-16** (в т.ч. § «Типичные ловушки», расширение таблицы ссылок). Существенное изменение норм — новая версия файла (`v2` и т.д.) или явная пометка в сообщении коммита; отдельный changelog не обязателен. |
| 113 | |
| 114 | |