Make Builder Design Pattern Reusable with Compile-Time Metaprogramming by Using Roslyn

Ahmet Murat Gençay
4 min readFeb 14, 2021

Design patterns are standardized problem-solving methodologies frequently used by software development processes. The biggest problem with design patterns is that we have to write codes repeatedly where we want to implement them.

In this article, I will create a solution using the Roslyn analyzer and the source generator, the latter being a new feature of C# 9.0 to make the Builder Design pattern reusable.

Basic Concepts (TL;DR)

Roslyn

Roslyn is the compiler platform for .NET. It consists of the compiler itself and a robust set of APIs to interact with the compiler.

Metaprogramming

Metaprogramming is a programming technique in which computer programs have the ability to treat other programs as their data. It means that a program can be designed to read, generate, analyze or transform other programs and even modify itself while running. In some cases, this allows programmers to minimize the number of code lines to express a solution, reducing development time. It also allows programs greater flexibility to handle new situations without recompilation efficiently.

Source Generators in C# 9.0

Source generators are a new feature of the C# compiler that enables inspecting user code using compiler-generated metadata and generating additional source files to be compiled along with the rest of the program.

Builder Pattern

The builder pattern is a design pattern designed to provide a flexible solution to various object creation problems in object-oriented programming. The Builder design pattern intends to separate the construction of a complex object from its representation. It is one of the Gang of Four design patterns.

Compile Time Metaprogramming

One of the most exciting features in .NET 5 and C# 9 are source generators. They enable us to hook into the compilation pipeline that is executed for our projects, analyze source files to be compiled, and provide additional source code for our final assembly. In other words, we can take advantage of compile-time metaprogramming!

Implementation

After learning the basics, I can start the implementation. First, I create a source generator class named BuilderGenerator, which implements the ISourceGenerator interface. Through this class, the compiler can generate code.

There are two builder design pattern services in the application, Ordered and Unordered.

Builder Dependency Diagram

The following filters I used when analyzing the classes to be processed;

  1. InputDocumentAttributeFilter: Used to filter classes that have Builder attribute.
  2. InputDocumentAbstractClassFilter: Used to generate errors in case of using Builder attribute in Abstract classes.
Filter Dependency Diagram

Dependency diagram of Builder Generator

With this structure, I had three builder approaches.

Unordered Builder Design Pattern

Basic builder pattern approach without any sorting features.

Unordered

Ordered Builder Design Pattern

The ordered builder pattern approach builds class instances according to the order of the class members.

Ordered

Ordered Builder Design Pattern with Partial Class

The ordered builder pattern approach builds class instances according to class members' order and can be accessed over the relevant class if it has the applied class partial keyword.

Ordered Partial Class

Summary

The Source Generator feature is excellent. We can reduce code duplications by generating code during compile time. Also, we can make many different adaptations according to our standards. The thing to remember here is that The Source Generator approach can generate new code, not edit an existing one.

References

  1. Design Patterns
  2. Roslyn Analyzer
  3. What’s new in C# 9.0
  4. Metaprogramming
  5. Source Generators
  6. Builder Pattern

--

--