What’s the Difference Anyway?! Class, Struct, Record, oh my!

This blog post is part of the 2021 C# Advent Calendar (the fifth year!). Please check out all of the other awesome posts in this years C# Advent.

You keep hearing about the record type in C# these days, and keep thinking to yourself, “how are those different from just a regular ‘ole class?” or, “how is a record different from a struct?”, or, “what the crap are these things anyway?”

Let’s talk about class first, because that’s probably what the majority of folx are familiar with.

First, some class highlights -

  1. A class is a reference type and an instance can be created by using the new operator or by assigning a compatible type.
  2. When an object is created from the type, memory is allocated on the managed heap, and the variable contains a reference to the instance of the type.
  3. Classes support inheritance from one base class (but that base class may inherit from another base class of its own, and so on).
  4. Uses reference equality, which means two instances refer to the same underlying object.

Now, a simple example of a “person” class. It just has two auto-implemented readonly properties -

using System;

public class Person
{
    public string FirstName { get; }
    public string LastName { get; }

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

Now that your type is defined, you can create an instance of it -

using System;

public class Program
{
    public static void Main()
    {
        var person1 = new Person("Calvin", "Allen");
        Console.WriteLine(person1.FirstName);
        Console.WriteLine(person1.LastName);
    }
}

Now let’s talk about the struct. A struct looks very similar to a class, but instead of being a reference type, a struct is considered a value type. Struct types are typically used for small, data-centric types that provide little to NO behavior. If behavior is important, then consider using a class instead.

Highlights!

  1. A struct is a value type and an instance can be created by using the new operator or by assigning a compatible type.
  2. When an object is created from the type, the variable contains an instance of the type (no memory is allocated on the managed heap).
  3. Structs implicitly inherit from System.ValueType (which inherit from object), but you can not have them inherit from your own base object (they can implement interfaces, however).
  4. Uses value equality, which means two objects share the same type, and contain the same value or values.

Since my previous example doesn’t have any behavior, let’s design it as a struct instead of a class

using System;

public struct Person
{
    public string FirstName { get; }
    public string LastName { get; }

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

Now that your type is defined, you can create an instance of it -

using System;

public class Program
{
    public static void Main()
    {
        var person1 = new Person("Calvin", "Allen");
        Console.WriteLine(person1.FirstName);
        Console.WriteLine(person1.LastName);
    }
}

The ONLY difference between the two examples is the declaration, and changing public class Person to public struct Person.

In my experience, most folx / companies use class more often than not, and the struct type is usually non-existant in the codebase. Having said that, your mileage may definitely vary depending on the type of application you’re building.

What about those new record types (introduced in C# 9, and enhanced again in C# 10 - I’m using C# 10 conventions in this post)? Well, they’re a lot like everything else we’ve seen so far - in fact, a record is really a class or struct “under the hood”, that just provides special syntax and behavior.

Let’s look at some highlights / differences between the other two first -

  1. A record, which is just shorthand in code for, record class, is a reference type, and a record struct is a value type. An instance can be created by using the new operator or by assigning a compatible type.
  2. Using positional parameters in a record class creates immutable properties. However, in a record struct, those same positional parameters would be entirely mutable. You can work around that by declaring your record struct as a readonly record struct.
  3. A record class can inherit from another record, but not from a class. Likewise, a class can’t inherit from a record. A record struct does not allow for inheritance like a traditional struct.
  4. Both record class and record struct use value equality, which means two objects must share the same type, and contain the same value or values.

Now, let’s look at an example (using positional parameters)-

using System;

public record class Person(string FirstName, string LastName);

Now that your type is defined, you can create an instance of it -

using System;

public class Program
{
    public static void Main()
    {
        var person1 = new Person("Calvin", "Allen");
        Console.WriteLine(person1.FirstName);
        Console.WriteLine(person1.LastName);
    }
}

Can you use a record class instead of a class? Maybe.

Can you use a struct instead of a record struct? Maybe. Can you use a class instead of a struct? Maybe.

The answer depends on the style you and your team utilize.

The answer depends on the version of C# you’re currently able to target.

The answer depends on how you feel about immutability.

And, since a lot of C#’ers also use Entity Framework, you cannot use a struct, record class, or record struct for your entities, since Entity Framework depends on reference equality, and these types utilize value equality.

Hopefully, though, after seeing some of the similarities and some of the differences, you have a better idea of when you can use one over the other.

I try not to “prescribe” specific solutions to you here, but simply provide enough data to help you make informed decisions. And, to be honest, there are more similarities and more differences the deeper you go - but I’ll leave that learning exercise to you :)

Thank you, dear reader, for following along on this years C# Advent. I sincerely hope you have a great holiday (whichever one you observe), a great New Year, and a fantastic 2022.

This post, “What’s the Difference Anyway?! Class, Struct, Record, oh my!”, first appeared on https://www.codingwithcalvin.net/whats-the-difference-anyway-class-struct-record-oh-my

33