July 1 2025
dotnet new classlib -n MySourceGenerator
dotnet add package Microsoft.CodeAnalysis.CSharp
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; using System.Text; [Generator] public class HelloWorldGenerator : ISourceGenerator { public void Initialize(GeneratorInitializationContext context) { // Register any callbacks here } public void Execute(GeneratorExecutionContext context) { // Create the source code to inject string sourceCode = @" using System; namespace HelloWorldGenerated { public static class HelloWorld { public static void SayHello() => Console.WriteLine(""Hello from the generated code!""); } }"; // Add the source code to the compilation context.AddSource("HelloWorldGenerated", SourceText.From(sourceCode, Encoding.UTF8)); } }
dotnet add reference ../MySourceGenerator/MySourceGenerator.csproj
// Program.cs using HelloWorldGenerated; class Program { static void Main(string[] args) { // Call the generated method HelloWorld.SayHello(); } }
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; using System.Text; [Generator] public class DIRegistrationGenerator : ISourceGenerator { public void Initialize(GeneratorInitializationContext context) { } public void Execute(GeneratorExecutionContext context) { var sourceCode = new StringBuilder(); sourceCode.AppendLine("using Microsoft.Extensions.DependencyInjection;"); sourceCode.AppendLine("namespace DIRegistrationGenerated"); sourceCode.AppendLine("{"); sourceCode.AppendLine(" public static class DIExtensions"); sourceCode.AppendLine(" {"); sourceCode.AppendLine(" public static IServiceCollection AddGeneratedServices(this IServiceCollection services)"); sourceCode.AppendLine(" {"); var compilation = context.Compilation; var interfaceType = compilation.GetTypeByMetadataName("MyNamespace.IMyService"); foreach (var typeSymbol in compilation.GetSymbolsWithName(name => name.EndsWith("Service"), SymbolFilter.Type)) { if (typeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.Interfaces.Contains(interfaceType)) { sourceCode.AppendLine($" services.AddTransient<{namedTypeSymbol.ToDisplayString()}>();"); } } sourceCode.AppendLine(" return services;"); sourceCode.AppendLine(" }"); sourceCode.AppendLine(" }"); sourceCode.AppendLine("}"); context.AddSource("DIRegistrationExtensions", SourceText.From(sourceCode.ToString(), Encoding.UTF8)); } }
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; using System.Text; [Generator] public class CompileTimeValidationGenerator : ISourceGenerator { public void Initialize(GeneratorInitializationContext context) { } public void Execute(GeneratorExecutionContext context) { var compilation = context.Compilation; var attributeSymbol = compilation.GetTypeByMetadataName("MyNamespace.MyCustomAttribute"); foreach (var classSymbol in compilation.GlobalNamespace.GetNamespaceMembers().SelectMany(ns => ns.GetTypeMembers())) { if (classSymbol.GetAttributes().Any(ad => ad.AttributeClass!.Equals(attributeSymbol, SymbolEqualityComparer.Default))) { var hasParameterlessConstructor = classSymbol.Constructors.Any(c => c.Parameters.IsEmpty); if (!hasParameterlessConstructor) { var diagnostic = Diagnostic.Create( new DiagnosticDescriptor( "GEN002", "Parameterless constructor required", "Class {0} marked with [MyCustomAttribute] must have a parameterless constructor.", "SourceGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true), Location.None, classSymbol.Name); context.ReportDiagnostic(diagnostic); } } } } }
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; using System.Text; [Generator] public class CustomSerializationGenerator : ISourceGenerator { public void Initialize(GeneratorInitializationContext context) { } public void Execute(GeneratorExecutionContext context) { var compilation = context.Compilation; var serializableAttribute = compilation.GetTypeByMetadataName("System.SerializableAttribute"); foreach (var classSymbol in compilation.GlobalNamespace.GetNamespaceMembers().SelectMany(ns => ns.GetTypeMembers())) { if (classSymbol.GetAttributes().Any(ad => ad.AttributeClass!.Equals(serializableAttribute, SymbolEqualityComparer.Default))) { var sb = new StringBuilder(); sb.AppendLine($"namespace {classSymbol.ContainingNamespace}.Generated"); sb.AppendLine("{"); sb.AppendLine($" public static class {classSymbol.Name}Serializer"); sb.AppendLine(" {"); sb.AppendLine($" public static string Serialize({classSymbol.Name} obj)"); sb.AppendLine(" {"); sb.AppendLine(" // Custom serialization logic"); sb.AppendLine(" return string.Empty;"); sb.AppendLine(" }"); sb.AppendLine(" }"); sb.AppendLine("}"); context.AddSource($"{classSymbol.Name}Serializer", SourceText.From(sb.ToString(), Encoding.UTF8)); } } } }
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; using System.Text; [Generator] public class ApiClientGenerator : ISourceGenerator { public void Initialize(GeneratorInitializationContext context) { } public void Execute(GeneratorExecutionContext context) { // Pretend this string is loaded from a Swagger specification var apiSpec = @" GET /api/values -> string[] POST /api/values -> void"; var sb = new StringBuilder(); sb.AppendLine("using System.Net.Http;"); sb.AppendLine("using System.Threading.Tasks;"); sb.AppendLine("namespace ApiClientGenerated"); sb.AppendLine("{"); sb.AppendLine(" public class ValuesApiClient"); sb.AppendLine(" {"); sb.AppendLine(" private readonly HttpClient _httpClient;"); sb.AppendLine(" public ValuesApiClient(HttpClient httpClient)"); sb.AppendLine(" {"); sb.AppendLine(" _httpClient = httpClient;"); sb.AppendLine(" }"); sb.AppendLine(" public async Task); sb.AppendLine(" {"); sb.AppendLine(" var response = await _httpClient.GetAsync(\"/api/values\");"); sb.AppendLine(" response.EnsureSuccessStatusCode();"); sb.AppendLine(" return await response.Content.ReadAsAsync); sb.AppendLine(" }"); sb.AppendLine(" public async Task PostValuesAsync(string[] values)"); sb.AppendLine(" {"); sb.AppendLine(" var response = await _httpClient.PostAsJsonAsync(\"/api/values\", values);"); sb.AppendLine(" response.EnsureSuccessStatusCode();"); sb.AppendLine(" }"); sb.AppendLine(" }"); sb.AppendLine("}"); context.AddSource("ValuesApiClient", SourceText.From(sb.ToString(), Encoding.UTF8)); } }
// MyClass.cs namespace MyNamespace { public class MyClass { public void MyMethod() { // Original method implementation } } }
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; using System.Text; [Generator] public class FileBasedGenerator : ISourceGenerator { public void Initialize(GeneratorInitializationContext context) { } public void Execute(GeneratorExecutionContext context) { // Read the content of MyClass.cs var sourceFile = context.AdditionalFiles.FirstOrDefault(file => file.Path.EndsWith("MyClass.cs")); if (sourceFile != null) { var fileContent = sourceFile.GetText(context.CancellationToken)?.ToString(); // Generate additional code based on the content of MyClass.cs if (fileContent != null) { var generatedCode = $@" namespace MyNamespace.Generated {{ public static class MyClassExtensions {{ public static void PrintInfo(this MyClass instance) {{ Console.WriteLine(""MyMethod was called in MyClass""); }} }} }}"; // Add the generated code to the compilation context.AddSource("MyClassExtensions", SourceText.From(generatedCode, Encoding.UTF8)); } } } } }
<!-- ConsumingProject.csproj --> <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> </PropertyGroup> <ItemGroup> <Compile Include="MyClass.cs" /> <AdditionalFiles Include="MyClass.cs" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\MySourceGenerator\MySourceGenerator.csproj" /> </ItemGroup> </Project>
// Program.cs using MyNamespace; using MyNamespace.Generated; class Program { static void Main(string[] args) { var myClass = new MyClass(); myClass.MyMethod(); // Use the generated extension method myClass.PrintInfo(); } }
1. Design Patterns that Deliver
This isn’t just another design patterns book. Dive into real-world examples and practical solutions to real problems in real applications.Check out it here.
Go-to resource for understanding the core concepts of design patterns without the overwhelming complexity. In this concise and affordable ebook, I've distilled the essence of design patterns into an easy-to-digest format. It is a Beginner level. Check out it here.
Every Monday morning, I share 1 actionable tip on C#, .NET & Arcitecture topic, that you can use right away.
Join 18,000+ subscribers to improve your .NET Knowledge.
Subscribe to the TheCodeMan.net and be among the 18,000+ subscribers gaining practical tips and resources to enhance your .NET expertise.