C# class vs struct


I see many people dealing with classes in C# without knowing the power of structs, this is more common for people who come from languages that do not have a definition for struct or people who start programming in C#.

As someone who comes from a C background, everything is very clear to me, so I will add to the existing library of posts and try to explain with my words.

Memory: class vs struct

Well, this is really complex. If you want an in-depth explanation please take a look at https://stackoverflow.com/questions/203695/does-using-new-on-a-struct-allocate-it-on-the-heap-or-stack
But as a general question, when you declare a variable that is a struct, this variable is created in the memory stack. Variables declared in sequence will have closer memory address since they will just be pushed one after the other (one of the reasons that Unity ECS needs you to create IComponentDatas as structs).

When you declare a variable of a class type, what will be created in the stack is a pointer to an instance of that class. When you create an instance of a class (new Something()) this instance will be created in the heap. When you assign "new Something()" to a variable, the variable will have a pointer to the heap where the instance was created (hence why Unity ECS doesn't allow you to create classes as IComponentData, that would make memory jumps in the heap).

Assignment: class vs struct


The assignment operator (=) deals differently with classes and struct. When you assign a struct (or any ValueType, like int, bool, float, double) to a variable, it creates a copy. Consider the following struct

struct Vector
{
    int x;
    int y;
    public Vector(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}

If you type

Vector v1 = new Vector(2,2);

The variable v1 will have the Vector(2,2) as it's value. If later you do:

Vector v2 = v1;

v2 will have a copy of v1. Each one points to a different memory address. This means that if I change any inner variable of v1, v2 will remain unchanged.

Vector v1 = new Vector(2,2);
Vector v2 = v1;
v1.x = 3; // (at this point, v1 is (3,2) and v2 is (2,2)) 

For classes, it behaves differently. If I declare the same struct, but as a class instead, the assignment of v2 = v1 will result in v2 having a reference to the same memory address of v1. So any change to either v1 or v2 will result in both changing since they point to the same instance.

class Vector
 {
     int x;
     int y;
     public Vector(int x, int y)
     {
         this.x = x;
         this.y = y;
     }
 }

Vector v1 = new Vector(2,2);
Vector v2 = v1;
v1.x = 3; // (at this point, v1 is (3,2) and v2 points to v1, which is (3,2))

In C# this is done automatically. The assignment of every instance of a class is done by reference and the assignment of every instance of a struct is done as a copy.

Comparison: class vs struct

When you compare instances of a class (==) they will be the same if they point to the same memory address. For instance, using the previously defined Vector class:

Vector v1 = new Vector(2,2);
Vector v2 = v1;
bool same = (v1 == v2) // true, since both are the same instance

Vector v1 = new Vector(2,2);
Vector v2 = new Vector(2,2);
bool same = (v1 == v2) // false, since each one is pointing to a different memory address, even if they have same values

However, when you compare structs what is being compared is the data (inner variables). So a struct will compare to true with another struct if both have the same values on all inner variables.

Vector v1 = new Vector(2,2);
Vector v2 = new Vector(2,2);
bool same = (v1 == v2) // if Vector is a struct, it will be true since both contain the same data.

Final Remarks


Although C# is a managed language all this stuff is done automatically by the compiler, it is possible to define pointers by using the unsafe environment. In C++, there you can explicit separate what you want to use as a reference and what you want to use as a value using * and &. In javascript and java (as far as I know, but I haven't really looked in-depth for this), everything is done with references.

Comments

Popular posts from this blog

Unity3D/C# - Asynchronous Programming - Coroutines vs await/async

C# - Simple Graph Interfaces and a MeshGraph

Unity/C# - Grids - Simple Grid Segment struct