This project demonstrates the use of .NET Source generators in order to automatically generate builder pattern for classes. Full details on this project are available in this blog post.
The code has been updated in 2024 since that blog was written to support new syntax constructs in C#. This builder implementation creates an immutable pattern that minimizes memory heap allocations and minimizes excessive cloning.
For code like this:
[GenerateBuilder]publicpartialclassDog{publicrequiredstringName{get;init;}publicstringBreed{get;init;}}It allows you to do this:
// start from initializer syntaxvardog=newDog{Name="Drake",Breed="Husky"};// start from builderdog=newDog.DogBuilder().WithName("Drake").WithBreed("Husky").Build();varanotherDog=dog.Builder.WithName("WallE").Build();// clone dog with new namevarbuilder=newDog.DogBuilder();builder.WithBreed("Husky").Build();// throws because required property Name is not setBy generating this:
partialclassDog{publicDogBuilderBuilder=>newDogBuilder(this);publicstructDogBuilder{privatebyte_set;Dog_original;publicDogBuilder(Dogoriginal){_original=original;}privatestring_name;publicDogBuilderWithName(stringname){_name=name;_set|=1;returnthis;}privateboolIsNameSet=>(_set&1)==1;privatestring_breed;publicDogBuilderWithBreed(stringbreed){_breed=breed;_set|=2;returnthis;}privateboolIsBreedSet=>(_set&2)==2;publicDogBuild(){if(_original==null){if(!IsNameSet){varmessage=$"The following required properties have not been set: {(!IsNameSet?"Name":"")}, ";thrownewInvalidOperationException(message.TrimEnd(',',' '));}returnnewDog{Name=_name,Breed=_breed};}if(IsNameSet&&!object.Equals(_name,_original.Name)){gotoclone;}if(IsBreedSet&&!object.Equals(_breed,_original.Breed)){gotoclone;}return_original;clone:returnnewDog{Name=IsNameSet?_name:_original.Name,Breed=IsBreedSet?_breed:_original.Breed};}}}