Roadmap › C# Language — Basics to Advanced › Module: Types in Depth
Lessons in this course (38)

16. Structs and enums in depth

BasicMediumLesson 16 of 38

Struct rules:

csharp
public readonly struct Temperature        // readonly struct: all fields immutable, compiler-enforced
{
    public double Celsius { get; }
    public Temperature(double celsius) => Celsius = celsius;
    public double Fahrenheit => Celsius * 9 / 5 + 32;
    public override string ToString() => $"{Celsius:0.#}°C";
}

var t = new Temperature(36.6);
Temperature empty = default;              // all fields zero — a struct is never null
Temperature? maybe = null;                // Nullable<Temperature> if you need "no value"
  • Value type: copied on assignment and when passed to methods (unless ref/in).
  • No inheritance (sealed, derive from System.ValueType), but can implement interfaces.
  • Can have constructors, properties, methods, operators; parameterless constructors and field initialisers allowed since C# 10.
  • Keep structs small and immutable; a mutable struct inside a collection or property leads to "I changed a copy" bugs.

Enum in depth:

csharp
public enum OrderStatus : byte            // underlying type (default int)
{
    Pending = 1,
    Paid = 2,
    Shipped = 3,
    Cancelled = 9
}

OrderStatus s = OrderStatus.Paid;
int code = (int)s;                                   // 2
OrderStatus fromCode = (OrderStatus)3;               // Shipped
string name = s.ToString();                          // "Paid"
bool ok = Enum.TryParse("shipped", ignoreCase: true, out OrderStatus parsed);
bool defined = Enum.IsDefined(typeof(OrderStatus), 5);   // false — casts don't validate!
foreach (var v in Enum.GetValues<OrderStatus>()) Console.WriteLine(v);

[Flags]
public enum Permission { None = 0, Read = 1, Write = 2, Delete = 4, All = Read | Write | Delete }
var p = Permission.Read | Permission.Write;          // combine with OR
p |= Permission.Delete;                              // add
p &= ~Permission.Write;                              // remove
bool canRead = (p & Permission.Read) != 0;           // or p.HasFlag(Permission.Read)
Console.WriteLine(p);                                // "Read, Delete"
  • Enum values are named constants of an integral type; [Flags] values must be powers of two so they can combine as bits.
  • Any integer can be cast to an enum, even undefined ones — validate input with Enum.IsDefined or a switch with a default.
  • Store enums in databases as int (stable) or string (readable); never reorder existing values.