Structs in C#

Structs

The Crash Course

  • Structs are very similar to classes. They can be created using the struct keyword instead of the class keyword.
  • Structs are created on the stack instead of the heap, making them faster, performance-wise, but it also means that they are passed by value instead of by reference.

Introduction

With an understanding of classes behind us, let's talk about a similar construct: the struct.

The name struct comes from the word "structured" and comes from C++ (which in turn got it from C). It represents a "structured" set of data or a "record". Structs in C were the beginnings of object-oriented programming and ultimately led to classes in C++, which were the basis for C#.

Structs are structurally very similar to a class. At first glance, it looks almost identical. But there is a key difference. A class defines a new reference type. As we saw earlier, reference types are allocated on the heap and you pass around references to them. A struct defines a new value type. These are types that do not have references, and passing them around leads to duplicating the memory for the thing instead.

Creating a Struct

So far, most things we've done with classes can also be done with structs. In fact, compare these two type definitions, the first of which is a struct and the second of which is a class:

struct TimeStruct
{
    public int Seconds { get; set; }
 
    public int CalculateMinutes()
    {
        return Seconds / 60;
    }
}
 
class TimeClass
{
    public int Seconds { get; set; }
    public int CalculateMinutes()
    {
        return Seconds / 60;
    }
}

Aside from the struct vs. class keyword, the two look identical. As mentioned a moment ago, the main difference is whether you've created a value type or a reference type. If it is a struct, it will be a value type. If it is a class, it will be a reference type. That can be a tricky nuance to understand and remember, so let's consider an example or two.

TimeStruct s = new TimeStruct();
s.Seconds = 120;
UpdateAndDisplayStruct(s);
 
TimeClass c = new TimeClass();
c.Seconds = 120;
UpdateAndDisplayClass(c);
 
static void UpdateAndDisplayStruct(TimeStruct timeStruct)
{
    timeStruct.Seconds += 10;
    Console.WriteLine(timeStruct.CalculateMinutes());
}
 
static void UpdateAndDisplayClass(TimeClass timeClass)
{
    timeClass.Seconds += 10;
    Console.WriteLine(timeClass.CalculateMinutes());
}

Because TimeClass is a reference type, when we pass c into UpdateAndDisplayClass, we copy the reference out of c and into the timeClass parameter. The main method and UpdateAndDisplayClass both have references to an object on the heap that they can both see and work with. Thus, when we modify the object in UpdateAndDisplayClass, it will affect the shared object, and the main method would be able to tell that it was changed.

In contrast, TimeStruct is a value type. When we pass s into UpdateAndDisplayStruct, we copy the entire chunk of data that is the TimeStruct, and we make a complete copy. UpdateAndDisplayStruct's timeStruct parameter will have a copy of the data, and when it modifies it, it will only modify its own copy.

Thus, we tend to get references to shared objects with classes, but with structs, that's not an option.

This is ignoring a fair bit of nuance. For example, a struct can have members, including fields and properties, that are reference types themselves (a struct can be made up of multiple reference types), and a class can have members that are value types (that part we've already seen a bunch). It gets a bit more tricky when you've got things intermixed like that because the struct may get copied, but if the struct contains references, those references will get duplicated, and we'll have duplicate references to the same objects. (Did I mention it was complicated?)

Structs are the simpler type, but classes are more powerful. Structs should generally be used when you want some small thing that is data-focused and relatively small. The size matters because structs will get copied around in all sorts of places, including passing one to a method, as we've seen here. If you have a struct with seventeen different members, that's a lot of data to copy. For example, if you want to represent a 2D point, making a Point struct may actually make more sense than a Point class. It will likely only have two fields (one for the x-coordinate and one for the y-coordinate) and is data-focused.

If you have a lot of behavior, a class is typically better than a struct.

In truth, most C# programmers use classes 99.9% of the time and almost never use structs. However, you will encounter structs quite often, so it is valuable to be aware of them and understand how they differ from a class.

What's Next?

Structs are conceptually similar to a class, with the main difference being that it is a value type instead of a reference type. You won't get references to a struct value (without some tricks and gimmicks). They're ideal for defining small, data-focused types but are otherwise not nearly as common as classes.

The next thing for us to look at is some of the more advanced features of classes. These features give us a whole lot of power in how we reuse code and organize our entire program. The first of these is inheritance.