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.
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:
struct Pointdeclares a struct with two fields,XandY.new Point()creates a point whose fields are all zero. For a struct,newdoes not create an object on the heap.p.X = 3andp.Y = 4write the coordinates right into the variablep: there is no reference to an object here.- 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.
1Point a = new Point();2a.X = 1;3a.Y = 2;4Point b = a;5b.X = 10;6Console.WriteLine(a.X + " " + b.X);7Spot c = new Spot();8c.X = 1;9c.Y = 2;10Spot d = c;11d.X = 10;12Console.WriteLine(c.X + " " + d.X);13 14struct Point { public int X; public int Y; }15class Spot { public int X; public int Y; }
new Point() creates a point of zeros right in the variable a.
The difference shows in the two output lines:
1 10: the structsaandbhave fields of their own, and the write tob.Xdid not reacha;10 10:canddlead to one object, and the write throughdis visible throughc.
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:
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:
Shift(start)copies the whole point into the parameterpoint.point.X = point.X + 10changes the copy: itsXbecomes13.- The method finishes, the copy disappears, and
start.Xprints3.
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:
List<Point> route = [new Point()];
route[0].X = 5; // ✗error CS1612: Cannot modify the return value of 'List<Point>.this[int]' because it is not a variableThe 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:
List<Point> route = [new Point()];
Point first = route[0];
first.X = 5;
route[0] = first;
Console.WriteLine(route[0].X); // 5An 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
structdeclares 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, solist[0].X = 5does not compile for aList.