В .NET, когда нужно избавиться от рукописного boilerplate, у вас есть богатый арсенал: рефлексия в рантайме, динамическая генерация IL (Reflection.Emit, System.Linq.Expressions), динамические прокси (Castle DynamicProxy) и — в современном мире — Source Generators, встроенные прямо в компилятор Roslyn. Значительная часть этой «магии» происходит в рантайме: сериализаторы изучают типы рефлексией, DI-контейнеры строят фабрики на лету, ORM генерируют прокси.
Go подходит к той же задаче — «как не писать руками повторяющийся код» — принципиально иначе. Идиоматичный Go избегает рантайм-магии и предпочитает кодогенерацию на этапе разработки: внешний инструмент порождает обычные .go-файлы, которые вы коммитите в репозиторий, читаете глазами и отлаживаете как любой другой код. Рефлексия в Go есть (reflect, и на ней стоят encoding/json и валидаторы из Разделов 5–6), но к ней относятся как к инструменту с ценником: за гибкость платят производительностью и потерей типобезопасности, поэтому в перформансном коде её стараются вытеснить генерацией.
Этот раздел — про философский водораздел: compile-time/dev-time генерация явного кода против runtime-рефлексии. Он короче остальных: три содержательные главы. Сначала разберём мотивацию (почему Go выбирает генерацию), затем механику (go:generate и экосистема инструментов), и в конце — детальное сравнение с миром .NET (Source Generators и Reflection.Emit).
- Понять, почему Go предпочитает кодогенерацию рефлексии: производительность, типобезопасность, читаемость и отладка, явность вместо магии.
- Знать, где рефлексия в Go всё же используется (
encoding/json, валидаторы) и почему её избегают на горячем пути. - Освоить директиву
//go:generateи понять, что Go лишь оркеструет внешние инструменты, а не генерирует код сам. - Познакомиться с ключевыми инструментами экосистемы:
stringer,mockery,protoc,sqlc, и со «смежной» compile-time фичейgo:embed. - Уметь устанавливать инструменты (
go install, директиваtoolвgo.modначиная с Go 1.24). - Чётко разграничить подход Go и C# Source Generators /
Reflection.Emit: что выполняется в рантайме, а что на этапе компиляции, и почему механика разная при схожей мотивации.
- Философия: генерация (compile-time) vs рефлексия (runtime) — почему Go выбирает явный сгенерированный код вместо рантайм-магии; где рефлексия всё же живёт и какова её цена.
go:generateи инструменты — директива//go:generate, командаgo generate, экосистема (stringer,mockery,protoc,sqlc), установка инструментов и сквозной пример.- Сравнение с .NET — C# Source Generators (Roslyn) и
Reflection.Emitпротив внешних генераторов Go; где что выполняется, сводная таблица и общий тренд к compile-time.
Держите таблицу под рукой при чтении раздела. Детали каждого сопоставления — в соответствующих главах.
| Механизм в .NET | Когда работает | Аналог/ответ в Go |
|---|---|---|
Рефлексия (System.Reflection) |
runtime | пакет reflect — есть, но избегается в горячем пути |
Reflection.Emit / DynamicMethod (генерация IL) |
runtime | отсутствует в идиоматичном Go; вместо этого — генерация кода до сборки |
System.Linq.Expressions (компиляция деревьев) |
runtime | нет прямого аналога; те же задачи решают кодогенерацией |
| Динамические прокси (Castle DynamicProxy) | runtime | сгенерированные моки (mockery) — на этапе разработки |
| Source Generators (Roslyn) | compile-time, в компиляторе | внешние генераторы + go generate — отдельный шаг, плоский текст |
| T4-шаблоны (Text Templates) | dev-time / build | go:generate + шаблонные генераторы (text/template) |
Сериализация через рефлексию (JsonSerializer по умолчанию) |
runtime | encoding/json (тоже рефлексия) или генераторы (easyjson и др.) |
System.Text.Json source-gen контекст |
compile-time | генераторы маршалинга (тот же мотив — убрать рефлексию) |
Встраивание ресурсов (EmbeddedResource + Assembly.GetManifestResourceStream) |
build + runtime | //go:embed — встраивание файлов на этапе компиляции |