TGViewer
yet another dev yet another dev @yet_another_dev · 382 subscribers
Post #163 382
👩‍💻 Генераторы кода в C#

English version.
Читать на сайте.

Возвращаюсь к работе после отпуска.

В ноябре я рассказывал про EventFlow — библиотеку, предназначенную для DDD и Event Sourcing. Наша команда активно её использует. Спустя несколько месяцев работы с ней я заметил, что часто приходится писать повторяющийся код, что довольно утомительно.

// Чтобы создать событие для агрегата OrderAggregate, 
// приходится каждый раз указывать тип агрегата и тип его ID
public class OrderCreated :
IAggregateEvent<OrderAggregate, OrderAggregateId>

// При подписке на события необходимо
// указывать тип события, агрегата и ID
public class OrderAggregateSubscribers :
ISubscribeSynchronousTo<OrderAggregate, OrderAggregateId, OrderCreated>


🔴Как избежать бойлерплейта?

Можно объявить абстрактный класс для всех событий агрегата и использовать его:

public abstract class OrderAggregateEvent : 
IAggregateEvent<OrderAggregate, OrderAggregateId>;


Создать новый интерфейс, принимающий только тип события:

public interface ISubscribeSynchronousTo<TEvent> : 
ISubscribeSynchronousTo<OrderAggregate, OrderAggregateId, TEvent>


Но даже в этом случае всё равно приходится вручную дублировать код для каждого агрегата. А как гласит известная айтишная мудрость:

«Никогда не тратьте 10 минут, чтобы сделать что-то вручную, если можно потратить 10 часов пытаясь это автоматизировать».


Поэтому в начале февраля я занялся написанием генератора кода для EventFlow. Через 2 недели и 7 коммитов библиотека EventFlow.SourceGenerators была готова и на днях Расмус Микелсен (автор EventFlow) принял изменения.

🔴Как помогают генераторы кода?

Теперь вся рутинная работа ложится на компилятор, и можно писать меньше кода, который к тому же лучше читается.

// Добавляем атрибут AggregateExtensions к агрегату
[AggregateExtensions]
public class OrderAggregate(OrderAggregateId id) :
AggregateRoot<OrderAggregate, OrderAggregateId>(id)

// Объявляем события
// OrderAggregateEvent создан автоматически
public class OrderCreated : OrderAggregateEvent;

// Подписываемся на события
// Новый интерфейс создан автоматически
public class OrderAggregateSubscribers :
ISubscribeSynchronousTo<OrderCreated>


Как видите, код стал заметно чище, и теперь нам не приходится снова и снова указывать тип агрегата и тип ID.

🔴Сложности при работе с генераторами кода

Стоит отметить, что генераторы кода — непростая тема. Существует множество ограничений и сложностей. Например, с чем я столкнулся:

1. Генераторы можно писать только под .NET Standard 2.0.
2. Возможны коллизии названий сгенерированных объектов.
3. Тестирование сгенерированного кода имеет свои особенности.

🔴Что дальше?

Если вам интересна тема генераторов кода, рекомендую начать с этого видео от Microsoft.

Затем изучите следующие материалы:

- Creating an incremental generator.
- Introducing C# Source Generators.
- Incremental Generators Cookbook.

В качестве примера можно заглядывать в исходный код библиотеки EventFlow.SourceGenerators и её тестов. Она довольна простая. Больше примеров её применения можно найти в документации.
  • 👍 14
More from @yet_another_dev
  1. Sep 21, 2026Опубликовал вчера ролик в одной запрещённой в России соцсети про то, как сходил на выборы.…
  2. Sep 20, 2026Мы пришли в 7:50 и очередь уже была 🥲 Пообщались с другими людьми. Многие приехали из дру…
  3. Sep 19, 2026Post #383
  4. Sep 18, 2026Последние пару недель на чат нападают боты со спамом (прикрыл стикером). Поэтому чат тепер…
  5. Sep 17, 2026Что интересного в этой статье: 1. Потрачено $120К, а агенты суммарно отработали около 3-х…
  6. Sep 17, 2026В Microsoft переписали рантайм GitHub Copilot с TypeScript на Rust при помощи агентов. Под…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →