Search code examples
c#.netdata-structuresmonogameentity-component-system

C# Returning Struct by ref results in copy


I tried to store and retrieve structs by reference using C#. What actually happened is that each struct that I attempted to retrieve was copied, and not returned by ref as instructed.

I have a "registry" class which holds an array of structs of which I am attempting to access and retrieve by reference. Unfortunately this does not appear to be happening as the returned result appears to be a copy of the element in within the array. I'm not sure how this is happening as every call in the call stack is set to return the value by ref.

ComponentArray.cs

        public ref T GetComponentByIndex(uint idx)
        {
            return ref m_componentArray[idx];
        }
        public ref T GetComponent(uint entityId)
        {
            if (!m_entityToIndexMap.ContainsKey(entityId))
            {
                //Throwings an exception here, might be a bit much. Better to just return null.
                throw new Exception("Entity does not have specified component");
            }

            return ref GetComponentByIndex(m_entityToIndexMap[entityId]);
        }
        public ref T Get(uint entityId)
        {
            return ref GetComponent(entityId);
        }

ComponentRegister.cs

        public ref T GetComponent<T>(uint entityId)
            where T : struct
        {
            var cmpArray = GetComponentArray<T>();

            return ref cmpArray.Get(entityId);
        }

EntityManager.cs

        public ref T GetComponent<T>(uint entityId)
            where T : struct
        {
            return ref m_componentRegister.GetComponent<T>(entityId);
        }

Entity.cs

        public ref T GetComponent<T>()
            where T : struct
        {
            return ref m_entityManager.GetComponent<T>(m_id);
        }

And here is the calling method :

        private GameObject CreateSpaceMarineArm(Texture2D spriteTexture)
        {
            var result = this.CreateGameObject("SM_Arm");

            result.AddComponent<Sprite>();
            var sprite = result.GetComponent<Sprite>();
            sprite.Initialise(spriteTexture, new Point(0, 160), 32, 2, 0.17f);
            sprite.Origin = new Vector2(0, 0);
            result.Transform.Position = new Vector2(0, -4);

            result.GetComponent<GameObjectData>().ShowDebugInfo = false;

            return result;
        }

I call .GetComponent<Sprite>() and then initialise the component...

However, when I call .GetComponent<Sprite>() a second time I can see that none of the changes I have made have been taken into account. Which tells me that I must be dealing with a copied struct.

The question is : Where did it copy?

Thanks in advance for any advice...


Solution

  • The copying is done on assignment from the method

     var sprite = result.GetComponent<Sprite>();
    

    If you write

    ref var sprite = ref result.GetComponent<Sprite>();
    

    It should work.

    If you are curious why is that you can write the two lines next to each other and use ILSpy to see the difference in generated IL.