C# struct reference behaviour

Hey guys, this is probably a stupid question but I don’t understand what’s going on.

I have a struct that I am saving in a dictionary and I have some component data in it, transform, meshrenderer, etc.

    // LOAD:  injects a chunk gameobject into the scene heirarchy
    public void Load(ref Chunk chunk)
    {
        chunk.loaded = true;
        chunk.transform = new GameObject("Chunk").transform;
        chunk.transform.parent = _.game.grid.transform;
        chunk.transform.position = new(chunk.position.x, chunk.position.y, chunk.position.z);

        chunk.filter = chunk.transform.AddComponent<MeshFilter>();
        chunk.renderer = chunk.transform.AddComponent<MeshRenderer>();
        chunk.collider = chunk.transform.AddComponent<MeshCollider>();
        chunk.filter.mesh = new Mesh();
        chunk.renderer.material = Assets.Material("node");
        chunk.collider.sharedMesh = chunk.filter.mesh;
    }

And I am grabbing the chunks like so.

    // GETCHUNK:  returns a reference to a chunk based on its position
    public static bool GetChunk(int3 position, out Chunk chunk)
    {
        if (_.game.grid.chunks.ContainsKey(position)) {
            chunk = _.game.grid.chunks[position];
            return true;
        } else {
            chunk = new Chunk();
            return false;
        }
    }

So the problem is when I grab the struct from the dictionary and change some values.

        _.GetChunk(position, out Chunk c);

        c.Load(ref c);
        Debug.Log(c.filter);
        Debug.Log(c.nodes.Count());
        c.nodes.Add(222, new Node());

        _.GetChunk(position, out Chunk c1);

        Debug.Log(c1.filter);
        Debug.Log(c1.nodes.Count());

The node count gets updated but the component data does not. Just returns null when I try grabbing it the second time.

Sorry for the stupid question… I’m just not sure what’s going on. Structs are allowed to hold component data right?

You should read up on reference vs value types in C#, this is really fundamental.

Basically, structs are copied while reference types pass around a reference to the same instance on the heap.

In your case, the chunks are copied when returned from the chunks collection and you only change a copy, the original chunk in the collection is never changed. You’d have to write back the changed chunk into the collection.

The nodes collection, on the other hand, is a reference type. Inside the struct, a reference to the same collection is copied around, but it always points to the same collection. So, if you change the referenced instance in one chunk copy, it will change for all copies.

But you probably don’t want to use structs anyway. They only make sense for small amounts of data and your chunk already seems pretty big (C# design guidelines recommend 16 bytes max. With some careful designing you can probably make bigger structs worthwhile but it’s difficult). As the struct gets bigger, the cost of copying it around all the time starts to outweigh the cost of garbage-collecting the reference object.

2 Likes

Thanks for reply Adrian. So when I use the out parameter…

_.GetChunk(position, out Chunk c);

It actually creates a copy then. Let’s say that I wanted to just change the values of the struct after I grab it from the dictionary without having to update the dictionary again. Do I need to use a pointer?

It’s not the out that is creating a copy here. It’s when you do this:
chunk = _.game.grid.chunks[position];.

Your comment here is innacurate: // GETCHUNK: returns a reference to a chunk based on its position, it’s simply modifying a reference to the Chunk struct that was created by the caller of GetChunk

The Dictionary only has the ability to return a copy of the struct, because it’s a struct.

There is no such thing as changing the values of the struct without updating the Dictionary, because the “original” struct data is part of the Dictionary itself. There is no way to do what you’re asking short of using a class instead of a struct.

2 Likes

Alrighty, thx man.