Forge
markdowne8ad0934
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
View only · write via MCP/CIDE