Skip to content

Instantly share code, notes, and snippets.

@HaloFour
Last active March 31, 2020 17:32
Show Gist options
  • Save HaloFour/bccd57c5e4f3261862e04404ce45909e to your computer and use it in GitHub Desktop.
Save HaloFour/bccd57c5e4f3261862e04404ce45909e to your computer and use it in GitHub Desktop.
Test inheritance with struct builders
/*
public record class Person {
public required int ID { get; init; }
public string FirstName { get; init; }
public string LastName { get; init; }
Person {
if (ID < 0) throw new ArgumentOutOfRangeException(nameof(ID));
}
}
public record class Student : Person {
public required double Gpa { get; init; }
Student {
// just an example, I understand it's possible for GPAs to exceed this range
if (Gpa < 0.0 || Gpa > 4.0) throw new ArgumentOutOfRangeException(nameof(Gpa));
}
}
*/
using System;
public class Person {
public int ID { get; }
public string FirstName { get; }
public string LastName { get; }
public Person(Builder builder) : this(null, builder) { }
protected Person(Person prototype, Builder builder) {
if (prototype != null) {
ID = builder.IDSet ? builder.ID : prototype.ID;
FirstName = builder.FirstNameSet ? builder.FirstName : prototype.FirstName;
LastName = builder.LastNameSet ? builder.LastName : prototype.LastName;
}
else {
if (!builder.IDSet) throw new InvalidOperationException();
ID = builder.ID;
FirstName = builder.FirstName;
LastName = builder.LastName;
}
Validate(ID, FirstName, LastName);
}
private void Validate(int ID, string FirstName, string LastName) {
if (ID < 0) throw new ArgumentOutOfRangeException(nameof(ID));
}
public static Builder NewBuilder() => default;
public virtual Person With(Builder builder) {
return new Person(this, builder);
}
public struct Builder {
private byte _set;
private int _id;
private string _firstName;
private string _lastName;
public int ID {
get => _id;
set {
_id = value;
_set |= 1;
}
}
public string FirstName {
get => _firstName;
set {
_firstName = value;
_set |= 2;
}
}
public string LastName {
get => _lastName;
set {
_lastName = value;
_set |= 4;
}
}
public bool IDSet => (_set & 1) == 1;
public bool FirstNameSet => (_set & 2) == 2;
public bool LastNameSet => (_set & 4) == 4;
}
}
public class Student : Person {
public double Gpa { get; }
public Student(Builder builder) : this(null, builder) { }
protected Student(Student prototype, Builder builder) : base(prototype, builder.Parent) {
if (prototype != null) {
Gpa = builder.GpaSet ? builder.Gpa : prototype.Gpa;
}
else {
if (!builder.GpaSet) throw new InvalidOperationException();
Gpa = builder.Gpa;
}
Validate(Gpa);
}
private void Validate(int ID, string FirstName, string LastName, double Gpa) {
// just an example, I understand it's possible for GPAs to exceed this range
if (Gpa < 0.0 || Gpa > 4.0) throw new ArgumentOutOfRangeException(nameof(Gpa));
}
public override Person With(Person.Builder builder) {
return new Student(this, new Builder() { Parent = builder });
}
public virtual Student With(Student.Builder builder) {
return new Student(this, builder);
}
public new struct Builder {
private Person.Builder _parentBuilder;
private byte _set;
private double _gpa;
public Person.Builder Parent {
get => _parentBuilder;
set => _parentBuilder = value;
}
public int ID {
get => _parentBuilder.ID;
set {
var temp = _parentBuilder;
temp.ID = value;
_parentBuilder = temp;
}
}
public string FirstName {
get => _parentBuilder.FirstName;
set {
var temp = _parentBuilder;
temp.FirstName = value;
_parentBuilder = temp;
}
}
public string LastName {
get => _parentBuilder.LastName;
set {
var temp = _parentBuilder;
temp.LastName = value;
_parentBuilder = temp;
}
}
public double Gpa {
get => _gpa;
set {
_gpa = value;
_set |= 1;
}
}
public bool IDSet => _parentBuilder.IDSet;
public bool FirstNameSet => _parentBuilder.FirstNameSet;
public bool LastNameSet => _parentBuilder.LastNameSet;
public bool GpaSet => (_set & 1) == 1;
}
}
@HaloFour
Copy link
Author

HaloFour commented Feb 11, 2020

class Program
{
    static void Main(string[] args)
    {
        /*
        Person person1 = new Student { ID = 123, FirstName = "First", LastName = "Last, Gpa = 4.0 };
        Person person2 = person1 with { FirstName = "FN", LastName = "LN" };
        */
        
        Person person1 = new Student(new Student.Builder() {
            ID = 123,
            FirstName = "First",
            LastName = "Last",
            Gpa = 4.0
        });

        Person person2 = person1.With(new Person.Builder() {
            FirstName = "FN",
            LastName = "LN"
        });

        Debug.Assert(person2 is Student { ID: 123, FirstName: "FN", LastName: "LN", Gpa: 4.0});
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment