C# from Basics to Mastery — Part 3
Understanding classes, structs, records and null safety
The articles written on this blog were created with the goal of teaching. Also, the information in the article may be incorrect, so please be careful.
In C#, data can be expressed as class, struct, and record. Even if the grammar is similar, the copy method and meaning of identity are different, so it must be selected according to the purpose.
class is a reference type
public sealed class Player
{
public string Name { get; }
public int Health { get; private set; }
public Player(string name, int health)
{
Name = name;
Health = health;
}
}
Player first = new("Rose", 100);
Player second = first;
In the second, references pointing to the same object are copied, not the entire object. If you change Health on one side, you can see the changed results on the other side.
struct is a value type
public readonly struct Position
{
public float X { get; }
public float Y { get; }
public Position(float x, float y)
{
X = x;
Y = y;
}
}
When you assign a struct, the value is copied. It is suitable for data that is small and treated as a single value, such as coordinates or colors.
Large structs have higher copying costs, and mutable structs can produce unpredictable behavior. A struct is not necessarily fast.
record compares the values themselves
public sealed record ItemKey(int Id, string Grade);
var a = new ItemKey(10, "Rare");
var b = new ItemKey(10, "Rare");
Console.WriteLine(a == b); // True
General classes basically compare whether references are the same, but record compares whether the configuration values are the same. It is good for use with immutable data such as settings, commands, and network messages.
Mark the type as nullable
Player? FindPlayer(int id)
{
return players.TryGetValue(id, out Player? player)
? player
: null;
}
Player? target = FindPlayer(10);
if (target is not null)
{
Console.WriteLine(target.Name);
}
Using a nullable reference type, you can distinguish between nullable and non-nullable references. Before clearing the warning with !, you need to check the flow again to see if null is truly impossible.
The choice of class, struct, and record is not a matter of preference. You must first decide whether the identity of the object is important, whether copying of values is natural, or whether it is immutable data.