In C# and .NET framework, there are two implementations of the concept of “class”, from the OOP point of view: classes and structures.
While we already know that classes are defined using the class keyword, structures are defined using the keyword struct.
The most important difference between a structure and a class is the fact that classes are reference types (references to heap addresses which hold their members), while structures (structs) are value types (they hold their members directly in the program’s execution stack).
Let’s take an example of defining a structure, using the most popular structure of .NET framework, the 2D point:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
struct Point2D { private double x; private double y; public Point2D(int x, int y) { this.x = x; this.y = y; } public double X { get { return this.x; } set { this.x = value; } } public double Y { get { return this.y; } set { this.y = value; } } } |
As you can see, the above code is exactly the same as a class, with the only noticeable difference of using the keyword struct instead of class. Point2D is a structure, a value type, so its instances behave like int and double. They are value types (not objects), which means they cannot be null and they are passed by value when taken as a method parameters.
To illustrate this we will play a bit with the Point2D structure:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
using System; namespace HelloWorld { class Program { static void Main(string[] args) { Point2D point = new Point2D(3, -2); PrintPoint(point); TryToChangePoint(point); PrintPoint(point); Console.Read(); } static void PrintPoint(Point2D p) { Console.WriteLine("({0},{1})", p.X, p.Y); } static void TryToChangePoint(Point2D p) { p.X++; p.Y++; } } struct Point2D { private double x; private double y; public Point2D(int x, int y) { this.x = x; this.y = y; } public double X { get { return this.x; } set { this.x = value; } } public double Y { get { return this.y; } set { this.y = value; } } } } |
When we compile and run the above code, the result is the following:
From this example, we can observe the demonstration of the fact that structures are value types. When they are passed as parameters to methods and functions, their fields are copied (the same way int parameters do), and when they are changed inside that method or function, the change does not affect the originals, just the copies.
First step, the point variable is created which holds a value of (3, -2). Next, the method TryToChangePoint(Point2D p) is called and it copies the value of the variable point into another place in the stack, allocated for the parameter p of the method. When the parameter p is changed in the method’s body, it is modified in the stack and this does not affect the original variable point which was previously passed as argument when calling the method.
If we change Point2D from struct to class, the result will be very different:
This is because the variable point will be now passed by reference (not by value) and its value will be shared between point and p in the heap.
How to decide when to use a class and when a structure? Here are some general guidelines:
- Use structures to hold simple data structures consisting of few fields that come together. Examples are: coordinates, sizes, locations, colors, etc.
- Structures are not supposed to have functionality inside (no methods, except simple ones like ToString() and comparators).
- Use structures for small data structures consisting of set of fields that should be passed by value.
- Use classes for more complex scenarios where you combine data and programming logic into a class. If you have logic, use a class.
- If you have more than few simple fields, use a class.
- If you need to pass variables by reference, use a class.
- If you need to assign a null value, prefer using a class.
- If you prefer working with a reference type, use a class.
- Classes are used more often than structures. Use structs as exception, and only if you know well what are you doing!
- All value types (int, bool, char) are structs. We rarely benefit from creating custom structs.
There are few other differences between class and structure in addition that classes are reference types and structures are values types, but I will not going to discuss them. For more details refer to the MSDN article.
Tags: class, object oriented programming, OOP, structures, value and reference types