Skip to content
Theory · Value and reference types · lesson 2 of 11

Structs

In the previous lesson, a value was copied like a photocopy, while an object got one more link, like a shared document. The built-in value types hold one value: a number, a character, true or false. But what if a value has several parts, like a point on a floor plan with two coordinates, and must be copied whole, like a sheet on a copier?

For this, C# has the struct—a value type of your own, declared with the word struct. It looks like a class: it has fields too, and it is declared after the top-level statements.

C#
Point p = new Point();
p.X = 3;
p.Y = 4;
Console.WriteLine(p.X + " " + p.Y); // 3 4

struct Point
{
    public int X;
    public int Y;
}

Let's go through the example line by line:

  1. struct Point declares a struct with two fields, X and Y.
  2. new Point() creates a point whose fields are all zero. For a struct, new does not create an object on the heap.
  3. p.X = 3 and p.Y = 4 write the coordinates right into the variable p: there is no reference to an object here.
  4. The last line prints 3 4.

In .NET, int, double and bool themselves are declared as structs too. That is why your struct is copied the same way as the built-in value types.

Assignment copies every field

Assigning a struct photocopies all of its fields at once. After Point b = a;, b has its own X and Y, and writing to b.X leaves a alone. With a class, the same line would give one object a second name.

In the widget, the two versions come one after the other: the struct Point and the class Spot with the same fields. A struct variable has its value written right in the box, while a class variable has an arrow → leading to an object. Press "Next step" and read the caption: it says whether the names share their data.

LiveA struct and a classstep 1 of 12 · frames: 1

new Point() creates a point of zeros right in the variable a.

The step caption says whether the names share their data

The difference shows in the two output lines:

  • 1 10: the structs a and b have fields of their own, and the write to b.X did not reach a;
  • 10 10: c and d lead to one object, and the write through d is visible through c.

Here the photocopy analogy is inexact. In real life, you can copy only the page you need. A struct, though, is copied whole: all its fields at once, even if you then change only one.

struct or class

Interviewers like to ask: how does a struct differ from a class? The main part of the answer is copying. Assigning a struct copies all its fields, while assigning a class copies only the reference.

This is also how you choose between them:

  • a struct suits a small value of a few fields that is natural to copy: a point, a color, an amount of money;
  • a class suits something that must be shared by everyone who works with it: a user, an order, a bank account;
  • copying a large struct costs more than copying a reference, so structs are kept small.

A struct as a method parameter

A method gets a copy of its argument, and for a struct that is a copy of every field. That is why a method without ref cannot move your point:

C#
Point start = new Point();
start.X = 3;
Shift(start);
Console.WriteLine(start.X); // 3

void Shift(Point point)
{
    point.X = point.X + 10;
}

Let's see what happens:

  1. Shift(start) copies the whole point into the parameter point.
  2. point.X = point.X + 10 changes the copy: its X becomes 13.
  3. The method finishes, the copy disappears, and start.X prints 3.

For a method to change the struct itself, you pass it with ref, the same way as an int variable.

Common mistake

Someone tried to change a point in a list by its index:

C#
List<Point> route = [new Point()];
route[0].X = 5; // ✗
TEXT
error CS1612: Cannot modify the return value of 'List<Point>.this[int]' because it is not a variable

The program does not compile. For a list, route[0] returns a copy of the point, not the point in the list itself. A write to a field of that copy would be lost at once, so the compiler forbids it. The this[int] in the message means access by an index in square brackets.

The fix is to read the point into a variable, change it, and write it back:

C#
List<Point> route = [new Point()];
Point first = route[0];
first.X = 5;
route[0] = first;
Console.WriteLine(route[0].X); // 5

An array has no such problem: points[0].X = 5 changes the point right inside the array, because an array element is a variable, not a copy.

Key points

  • struct declares a value type of your own: the value is stored right in the variable, with no reference to an object.
  • Assigning a struct copies every field; assigning a class copies only the reference.
  • A method gets a copy of a struct and, without ref, does not change the caller's variable.
  • list[0] returns a copy of a struct, so list[0].X = 5 does not compile for a List.