In 2016 I submitted a C# proposal for Buildable Immutable Types to the Roslyn team. The feedback I got from other community members was that my proposal would work better as a T4 Template or as part of the "upcoming Source Generation" feature.
Now, 4 years later, with Source Generators finally coming to C# I thought it would be the right time to follow up on it.
The initial replies I received on GitHub were a little disappointing, but considering that the same people who poo-pooed the suggestions also created this issue 2 years later suggests that the core idea is good (especially considering how welcomed their proposal was). This seems to support my concern that dealing with the construction of immutable types is a painful process in C#.
I have reproduced the original proposal below. Next time, I'll describe my experiencing developing the source generator.
The proposal, as originally written
Like the rest of you, I try to create immutable types where possible. Consider the simple Person
class below:
public class Person
{
public Person(string name)
{
Name = name;
// ... set other properties
}
public string Name { get; }
// ... define properties
}
As the number of fields grow, the parameters start to get quite out of control. Now consider that the properties of Person
must be calculated from a variety of sources. In our build method we can either have variables all over the place, or we must create a new PersonBuilder
class which stores properties and instantiates the Person
class.
// Creates a person based on some fancy logic
public Person CreatePersonUsingVariables(...)
{
var name = ...;
// complex computations of various properties
// this logic can have branches which set different properties
// or might call to methods.
return new Person(name, ...);
}
// Creates a person based on a builder class
public Person CreatePersonUsingBuilder(...)
{
var builder = new PersonBuilder();
builder.Name = ...;
// set properties when needed, can pass builder around to other methods
return builder.Build();
}
// Builder (have to define manually)
public class PersonBuilder
{
public string Name { get; set; }
// ... define properties
public Person Build() => new Person(name, ...);
}
The drawbacks of creating your own builder class are related to maintainability and overhead. Many times developers will just forego immutability in order to save time having to defines these builder types.
My proposal is for C# to automatically create a builder class. Syntax would look something like this:
// C# code
public buildable class Person
{
string Name { get; }
}
// Compiler generates
public class Person
{
public Person(string name)
{
Name = name;
// ... set other properties
}
public string Name { get; }
// ... define properties
public class Builder
{
public string Name { get; set; }
// ... define properties
public Person Build() => new Person(name, ...);
}
}
Things to note:
The modifier
buildable
lets the compiler know how to treat this.Properties on buildable types are implicitly public. Like interfaces no modifier is allowed.
The compiler adds a public nested class within the buildable type called "Builder".
In all other ways the compiler can treat
Person
andPerson.Builder
as regular types.We can add methods to the
Person
type. If methods onPerson.Builder
are needed, the following syntax can be used:// C# code public buildable class Person { string Name { get; } // Methods applicable to Person public void Eat() { // In this scope, properties are readonly } // I can choose to override GetHashCode() and Equals(object) builder // Defines a scope for methods of Person.Builder { public void CalculateAge(DateTime dob) { // In this scope, properties are mutable } } }
We can also have a method which generates the builder from the type.
// Compiler generated, for brevity I have excluded code from previous listing public class Person { public Builder GetBuilder() { return new Builder { Name = name, // ... other properties }; } }
Optional: We can extend this to buildable structs
Join the Discussion
You must be signed in to comment.