C#SeniorTechnical
Что такое source generators в C# и какие проблемы они решают?
Source generators — Roslyn-компоненты, генерирующие C#-код во время компиляции. Устраняют runtime-reflection в сериализаторах, DI-контейнерах и маппинге, давая нулевой overhead в production.
Source Generators в C#
Source generators — это компоненты компилятора Roslyn, которые запускаются во время сборки, анализируют синтаксическое дерево исходного кода и добавляют новые файлы .cs в компиляцию. Сгенерированный код становится частью сборки — без reflection, без emit, без overhead во время выполнения.
Какие проблемы решают
- Производительность сериализации —
System.Text.Jsonsource generation вместо runtime reflection. - DI-регистрация — автосканирование типов без
Assembly.GetTypes()при старте. - Маппинг объектов — Mapperly генерирует typed маппер вместо AutoMapper с reflection.
- Логирование —
LoggerMessage.Definesource gen для zero-allocation логов. - NativeAOT-совместимость — reflection недоступно или обрезается триммером; source gen даёт статический код.
System.Text.Json Source Generation
// 1. Определяем модель
public record OrderDto(int Id, string Product, decimal Price);
// 2. Создаём JsonSerializerContext с атрибутом
[JsonSerializable(typeof(OrderDto))]
[JsonSerializable(typeof(List<OrderDto>))]
public partial class AppJsonContext : JsonSerializerContext { }
// 3. Используем при сериализации
var order = new OrderDto(1, "Laptop", 999.99m);
string json = JsonSerializer.Serialize(order, AppJsonContext.Default.OrderDto);
// Регистрация в ASP.NET Core
builder.Services.ConfigureHttpJsonOptions(opts =>
opts.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default));
Structured Logging с LoggerMessage Source Gen
public partial class OrderService
{
private readonly ILogger<OrderService> _logger;
public OrderService(ILogger<OrderService> logger) => _logger = logger;
// Компилятор генерирует zero-allocation метод
[LoggerMessage(Level = LogLevel.Information, Message = "Order {OrderId} created by {UserId}")]
private partial void LogOrderCreated(int orderId, string userId);
public void CreateOrder(int id, string userId)
{
// ...
LogOrderCreated(id, userId); // нет boxing, нет аллокаций
}
}
Написание собственного Source Generator
[Generator]
public class HelloGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// Ищем классы с атрибутом [GenerateGreeting]
var classes = context.SyntaxProvider
.ForAttributeWithMetadataName(
"GenerateGreetingAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => (INamedTypeSymbol)ctx.TargetSymbol)
.Where(t => t is not null);
context.RegisterSourceOutput(classes, (spc, type) =>
{
var source = $$"""
namespace {{type.ContainingNamespace}};
partial class {{type.Name}}
{
public string Greet() => "Hello from {{type.Name}}!";
}
""";
spc.AddSource($"{type.Name}.g.cs", source);
});
}
}
Подводные камни
- Source generators работают только в
IIncrementalGenerator(v2) — старыйISourceGeneratorмедленнее и устарел. - Генератор не может изменять существующий код — только добавлять новые файлы; для патчинга используйте
partialклассы/методы. - Ошибки в генераторе приводят к непонятным ошибкам компиляции — всегда добавляйте диагностические дескрипторы (
DiagnosticDescriptor). - Горячая перезагрузка в Rider/VS может работать нестабильно с генераторами — иногда требуется полный rebuild.
- Сгенерированные файлы не отображаются в проекте по умолчанию — смотри
obj/Generated/или включи<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>в.csproj. - Тестирование генераторов требует отдельного проекта с
Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing. - Зависимости генератора (
AnalyzerReference) не должны тянуть тяжёлые пакеты — это замедляет компиляцию.
Common mistakes
- Путать source generators и генерация кода на этапе компиляции с похожим механизмом из другой версии или платформы.
- Игнорировать runtime-границы C#: lifecycle, DI scope, SQL translation, UI thread или platform API.
- Не обсуждать null/empty/error cases и поведение под нагрузкой.
What the interviewer is testing
- Кандидат объясняет source generators и генерация кода на этапе компиляции на конкретном примере, а не только определением.
- Указывает последствия для производительности, тестируемости и поддержки.
- Различает документированное поведение текущего стека и устаревшие практики.