The tutorial to rule ALL tutorials on structures (struct) in C#
public struct Employee
{
public string Name { get; set; }
public string Department { get; set; }
public override string ToString()
{
return Name + " works in " + Department;
}
}
What is the struct keyword?
The struct keyword in C# is used for creating a structure.
A structure is similar to a class and is used to hold values of different types under one home.
Like a class, it can contain a static constructor, a parameterized constructor, constants, fields, methods, properties, operators, indexers, nested types, and events.
Its purpose is to represent a record. A blueprint that defines the variables to describe an object. For example, an employee structure may store basic employee details like “Name” and “Department”.
Despite its similarity to a class, there are subtle differences that we will explore in this article.
What are structures used for in C#?
Structures are suitable for storing simple, low value objects that tend to be manipulated often, such as ints or bools.
But don’t classes do that already? The raison d'être of structures lies in one word: performance.
That's because of how they manage memory. Structs are value types whereas classes are reference types. Classes are allocated separate memory on the managed heap whereas structs occupy the evaluation stack.
Don't worry if that didn't make a great deal of sense! What it means in practical terms is that structs do not require the new keyword for memory allocation on the heap like classes do. They act like a value type, such as an int.
This saves time when retrieving or modifying your variables, as they are accessed directly without needing to fetch them via a reference.
This does have some implications, as we'll see later.
This time improvement is not really going to make a difference for many of your usual application use cases, but it can prove crucial for programs where speed is critical, such as in video games.
So, shouldn’t I always use a struct instead of a class?
No, there are several limitations to structs, and generally, you will use classes unless your application needs you to measure and track performance.
How to use the struct keyword?
Here is a small console program containing employee data and a console program that creates and populates some structs.
Limitations
Structures tend to be more performant than classes, but what are the trade-offs?
Structures cannot inherit from another structure or from a class
There is one exception - the base Object type, from which it inherits automatically.
This means that we can inherit from the few Object methods like .ToString(). Let's try it:
public struct Employee
{
public string Name { get; set; }
public string Department { get; set; }
public override string ToString()
{
return Name + " works in " + Department;
}
}
This lets us do something like:
Employee federica = new Employee() { Name = "Federica", Department = "Bio-Sciences" };
Console.WriteLine(federica.ToString());
OUTPUT:
Federica works in Bio-Sciences
Feel free to give it a try yourself and modify the fiddle above.
Structures can't use default constructors
Like classes, it is possible to have constructors on a structure. However, historically (prior to C# 10), this didn't include the default parameterless constructor.
public struct Employee
{
// A default constructor will trigger a compilation error in older C# versions
public Employee()
{
}
}
However, include a parameter and this works:
public struct Employee
{
public string name;
public Employee(string name)
{
this.name = name;
}
}
Employee employee = new Employee("Benjamin");
Object fields & properties must be initialised before using them in the constructor
Be careful using a property in the constructor.
public struct Employee
{
private int employeeNumber { get; set; }
public Employee(int employeeNumber)
{
AssignNumber(employeeNumber);
}
private void AssignNumber(int number)
{
employeeNumber = number;
}
}
The above code will cause the following compilation errors:
The 'this' object cannot be used before all of its fields are assigned to.
Backing field for automatically implemented property 'Employee.employeeNumber' must be fully assigned before control is returned to the caller. Consider calling the default constructor from a constructor initializer.
To correct this error, you can change the constructor definition and call the parameterless constructor from your parameterized one by doing: : this().
The default constructor takes care of initializing all the variables of a class or structure.
public struct Employee
{
private int employeeNumber { get; set; }
public Employee(int employeeNumber) : this()
{
AssignNumber(employeeNumber);
}
private void AssignNumber(int number)
{
employeeNumber = number;
}
}
Good struct practice dictates that they should be 16 bytes or less and immutable. If you are going to change object fields after creating them, consider refactoring your struct to a class.
Modifying structures in parameters must use ref directly
A struct is a value type, and each time we pass a structure into the parameters of a method, a copy of the object will be made.
Observe the following code:
static void Main(string[] args)
{
// Person is our struct
Person federica = new Person() { Age = 28 };
IncreaseAge(federica);
Console.WriteLine(federica.Age);
}
private static void IncreaseAge(Person person)
{
person.Age++;
}
This code will still output 28 despite altering the age of the person in the method IncreaseAge.
That's because the IncreaseAge method is working on a copy of the structure.
This of course means that if we want to change a structure from a method, we will have to use the keyword ref:
static void Main(string[] args)
{
Person nicolas = new Person() { Age = 30 };
IncreaseAge(ref nicolas);
Console.WriteLine(nicolas.Age);
}
private static void IncreaseAge(ref Person person)
{
person.Age++;
}
This applies to all value types.
Be careful, though. If the structure is very large, making a copy of it every time you use a method can be particularly time-consuming and can disrupt your application's performance.
Differences between Class and Structure
Some of these differences were covered in the limitations section.
- Classes support inheritance, whereas structures do not.
- Classes can have both a default and defined constructor, whereas structures heavily rely on defined parameterized constructors (though recent C# versions have relaxed this).
- Classes are reference data types, whereas structures are value data types.
- Classes can have protected data members, whereas structures can only have public or private data members.
When should we use a struct over a class?
We can turn straight to Microsoft's .NET documentation for guidance.
What it boils down to is this:
Define a structure if:
- The instances of the type are small, similar to primitive types (integer, double, bool).
- You want value semantics as opposed to reference semantics.
- It is immutable.
- It has an instance size smaller than 16 bytes.
- It doesn't need to be boxed frequently.
- There is no need for polymorphism.
- You want to avoid heap allocation and the related garbage collection overhead.
Keep in mind that when it comes to performance, classes could still end up faster in practice if you are passing structs as arguments frequently, as copying value types is more expensive than passing class references.