I writing a lot of C# (Unity3D) for game project I am working on currently. There are tasks, where you don't want to use reference type (classes) to allocate and mutate chunk of data on heap. I had a concrete case: handle message from remote server and update list of containers with new info on client side. My decision was to create list with structs and then update corresponding objects while receiving message from remote server.

I wrote a bit of pseudo code to demonstrate what I did at first:

using System.Collections.Generic;
					
public class Program
{
	public struct PlayerInfo 
    {
        public bool isOnline;
        
        public PlayerInfo(bool onlineStatus) 
    	{
		isOnline = onlineStatus;
	}
    }

    public static void Main(string[] args)
    {
        List<PlayerInfo> playersData = new List<PlayerInfo>();

        for (int i = 0; i < 20; i++)
        {
            var obj = new PlayerInfo(false);
            playersData.Add(obj);    
        }   


	//update online status at some point
        for (int i = 0; i < playersData.Count; i++)
        {
        	var onlineStatus = playersData[i].isOnline;
            playersData[i].isOnline = !onlineStatus;
        }
    }
}

This won't compile and will throw next error:


Cannot modify the return value of 'System.Collections.Generic.List<Program.PlayerInfo>.this[int]' because it is not a variable


I understand that struct is a value type and isOnline field returns copy. It means that modification is not possible in that way. The thing I do not understand is why C# compiler would not do the job under the hood assigning new value to proper memory address. With such syntax I definitely want to write the proper value to the memory. I would be even fine with warning throwing along the way notifying that what I did is actually changed real value of value type. As if I would NOT WANT to change that value, I would create ==readonly== field.

Instead of it, C# urges user to write setter for that value. Though, you can't use usual C# get / set syntax like that:

public struct PlayerInfo {
    
    	public bool isOnline;
    
    	public bool IsOnline {
		get { return isOnline; }
		set { isOnline = value; }
	}
}

Error would stay the same and code would not compile using IsOnline getter / setter for our operation.

What user actually needs to do is to extend structure with custom setter like this:

public struct PlayerInfo {
    
    public bool isOnline;
    
    public PlayerInfo(bool onlineStatus) {
	isOnline = onlineStatus;
	}
	
	public void SetOnline(bool onlineStatus) {
		isOnline = onlineStatus;
	}
}

To be honest I do not like writing these setters as I think it's just unnecessary typing and make code base bigger with no particular reason for it. If not let compiler just optimize that for user, I would then use C like approach with pointer syntax when working with value types in C#. Language already has this syntax, though it serves different purposes when working with unsafe code.

Many times I encountered solutions on internet forums when programmers just reallocate struct fully, like doing playersData[i] = new PlayersData(newOnlineStatus). To be honest, I do not think that C# compiler would optimize that and would not actually allocate new structure on stack. I would personally not write such solutions.

Why not just use class? Well, because you want stack memory speed when changing data frequently.

  1. Struct is ideal tool for this problem as official page says.
  2. Class container (reference type) allocation / memory write will always work slower in C#.

By the way, I probably became that boring because of reading through projects on Handmade Network community, lol. I like them a lot!