Skip to content

Records C# 9.0code reductioncorrectness

Declare immutable types and eliminate boilerplate code.

A record behaves like a class in that it is a reference type however the compiler provides automatic equality and cloning much like it does for a struct (a value type).

Code

C#
record Person
{
    public string LastName { get; }
    public string FirstName { get; }

    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }
}
C#
class Person : IEquatable<Person>
{
    public string FirstName { get; private init; }
    public string LastName { get; private init; }

    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }

    public override int GetHashCode()
    {
        int hash = 17;
        if (FirstName != null) hash = hash * 23 + FirstName.GetHashCode();
        if (LastName != null) hash = hash * 23 + LastName.GetHashCode();
        return hash;
    }

    public bool Equals(Person other)
    {
        if (Object.ReferenceEquals(this, other)) return true;
        if (other == null || other.GetType() != typeof(Person)) return false;
        return other.FirstName == FirstName && other.LastName == LastName;
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as Person);
    }

    public static bool operator ==(Person a, Person b)
    {
        if (Object.ReferenceEquals(a, null))
        return Object.ReferenceEquals(b, null);
        return a.Equals(b);
    }

    public static bool operator !=(Person a, Person b)
    {
        return !(a == b);
    }
}

Notes

  • Extended to struct in C# 10.0](/10.0/record-structs)

When declaring a record the compiler also produces support for:

  • cloning via with
  • a default implementation of ToString that prints the value of each member
  • a new EqualityContract property
  • a Deconstruct method for supporting deconstruction patterns

More information