Unexpected intialization in struct as opposed to class

Hello everyone.

I have the following code that is supposed to keep track of a group of elements, and whenever they all die, send a notification. It works by counting how many elements there are in the beginning, decreasing a counter every time one is destroyed, and sending a notification once the counter reaches 0.

It’s pretty straightforward and works fine when Notification is a class. However, when I change Notification to be a struct, it doesn’t.

If I look at output, with a class I get the following output:
(at start) Notification myNotification has 3 elements
(at update) Notification myNotification has 3 elements

if I change to a struct, I get the following:
(at start) Notification myNotification has 3 elements
(at update) Notification myNotification has 0 elements

How is that possible? That is before anything is even called, even if I remove the function DecreaseCount() the variable currentCount is set to 0 after being initialized to 3 at the very start. Is this some default construction of structs I’m not aware of?

Thank you for your help

public class NotificationManager : MonoBehaviour
{
    public Notification[] notifications;

    void Start()
    {
        foreach (Notification n in notifications) n.Setup();
    }

    void Update()
    {
        foreach (Notification n in notifications) n.Print();
    }

   
    [System.Serializable]
    public /*struct*/ class Notification
    {
        public string name;
        public Element[] elements;

        private int currentCount;

        public void Setup()
        {
            foreach (Element element in elements)
            {
                currentCount++;
                element.OnDeath += DecreaseCount;
            }
            Print();
        }

        public void Print()
        {
            Debug.Log($"Notification {name} has {currentCount} elements");
        }

        public void DecreaseCount()
        {
            currentCount--;
            if (currentCount == 0)
            {
                // notify
            }
        }
    }
}

That sounds realy weird,

i was looking up at the microsoft doc, and maybe this information will anyway change your mind of using struct in your case since it looks to me that your “class” is not “short living”

To mutate structs in arrays/lists, you need to make a local copy, mutate the struct, and then save it back to the array/list. n.Setup() just modifies the local copy of the foreach loop, not the one in the array.

If you try to change a field of the struct directly during the foreach loop, C# will generate an error. But if the field assignment is hidden inside a method of the struct, there’s not even a warning.

This is why in most non-trivial cases, you can’t just turn a class into a struct. You’ll have to check all cases where the instance is modified and make sure the changes are properly written back.

2 Likes