Isaac.

csharp

C# Source Generators

Reduce boilerplate with compile-time code generation.

By Emem IsaacMarch 28, 20223 min read
#csharp#source generators#code generation#metaprogramming
Share:

A Simple Analogy

Source Generators are like hiring someone to write repetitive code for you. You describe what you want, and the compiler generates it automatically during build.


What Are Source Generators?

Source Generators run at compile-time, analyzing your code and generating new C# code. No runtime reflection needed—pure compile-time magic.


Why Use Source Generators?

  • Zero runtime cost: Generation happens at build
  • Type safety: All errors caught at compile-time
  • Boilerplate elimination: Auto-generate repetitive code
  • Better performance: No reflection overhead
  • IDE support: Generated code is visible

Basic Generator Structure

[Generator]
public class AutoNotifyGenerator : ISourceGenerator
{
    public void Initialize(GeneratorInitializationContext context)
    {
        // Register receivers if needed
    }

    public void Execute(GeneratorExecutionContext context)
    {
        // Find classes marked with [AutoNotify]
        var compilation = context.Compilation;
        var symbols = compilation.GlobalNamespace
            .GetMembers()
            .OfType<INamedTypeSymbol>();

        foreach (var symbol in symbols)
        {
            if (HasAttribute(symbol, "AutoNotify"))
            {
                var source = GenerateNotifyCode(symbol);
                context.AddSource($"{symbol.Name}_AutoNotify.g.cs", source);
            }
        }
    }

    private string GenerateNotifyCode(INamedTypeSymbol symbol)
    {
        var properties = symbol.GetMembers()
            .OfType<IPropertySymbol>();

        var code = new StringBuilder();
        code.AppendLine($"partial class {symbol.Name} : INotifyPropertyChanged");
        code.AppendLine("{");
        code.AppendLine("    public event PropertyChangedEventHandler PropertyChanged;");

        foreach (var prop in properties)
        {
            code.AppendLine($"    private {prop.Type} _{Char.ToLowerInvariant(prop.Name[0])}{prop.Name.Substring(1)};");
        }

        code.AppendLine("}");
        return code.ToString();
    }
}

Usage Example

// Mark class with attribute
[AutoNotify]
public partial class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

// Generator creates:
// partial class Person : INotifyPropertyChanged
// {
//     public event PropertyChangedEventHandler PropertyChanged;
//     private string _firstName;
//     // ... rest of implementation
// }

Practical Example: Dependency Injection

[Generator]
public class ServiceProviderGenerator : ISourceGenerator
{
    public void Execute(GeneratorExecutionContext context)
    {
        var compilation = context.Compilation;
        var services = FindServicesWithAttribute(compilation, "Singleton");
        
        var code = GenerateServiceProvider(services);
        context.AddSource("GeneratedServiceProvider.g.cs", code);
    }

    private string GenerateServiceProvider(List<ServiceInfo> services)
    {
        var sb = new StringBuilder();
        sb.AppendLine("public class GeneratedServiceProvider : IServiceProvider");
        sb.AppendLine("{");
        
        foreach (var service in services)
        {
            sb.AppendLine($"    public {service.Interface} Get{service.Name}()");
            sb.AppendLine("    {");
            sb.AppendLine($"        return new {service.Implementation}();");
            sb.AppendLine("    }");
        }
        
        sb.AppendLine("}");
        return sb.ToString();
    }
}

Best Practices

  1. Keep generators simple: Focus on one task
  2. Provide diagnostics: Help users fix issues
  3. Document behavior: Explain what gets generated
  4. Test thoroughly: Generate correct code
  5. Version carefully: Changes affect compilation

Related Concepts

  • Incremental generators for performance
  • Syntax trees and semantic analysis
  • Analyzer integration
  • Code generation frameworks (T4)

Summary

Source Generators eliminate boilerplate through compile-time code generation. Use them to auto-generate serializers, dependency injection containers, and notification implementations.

Share:

Written by Emem Isaac

Expert Software Engineer with 15+ years of experience building scalable enterprise applications. Specialized in ASP.NET Core, Azure, Docker, and modern web development. Passionate about sharing knowledge and helping developers grow.

Ready to Build Something Amazing?

Let's discuss your project and explore how my expertise can help you achieve your goals. Free consultation available.

💼 Trusted by 50+ companies worldwide | ⚡ Average response time: 24 hours