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.IsDefinedor aswitchwith a default. - Store enums in databases as int (stable) or string (readable); never reorder existing values.