Skip to content

Properties and encapsulation

↑

Examples are independent C# snippets for modern .NET console projects unless noted otherwise. Class declarations and top-level code can live in Program.cs when top-level statements come first.

A property is a controlled way to expose data or computed values. Unlike a field, a property can run logic when data is read or assigned.

get reads a value; set writes it. private set lets other classes read a property while only the containing class can update it. This helps keep objects in a valid state.

Example.cs
public class Player
{
public string Name { get; init; } = "Unnamed";
public int Health { get; private set; } = 100;
public void Damage(int amount)
{
if (amount < 0) throw new ArgumentOutOfRangeException(nameof(amount));
Health = Math.Max(0, Health - amount);
}
}

A full property can use a private backing field to reject invalid input. Favor clear validation and avoid expensive or surprising side effects inside getters.

Example.cs
public class VolumeSettings
{
private int volume;
public int Volume
{
get => volume;
set => volume = Math.Clamp(value, 0, 100);
}
}

init permits setting a property during object initialization but not later in ordinary code. Unity built-in serialization generally serializes fields, not ordinary C# properties; expose a private [SerializeField] field and a read-only property when editing in Inspector.

Example.cs
// Inside a Unity MonoBehaviour:
[SerializeField] private int maxHealth = 100;
public int MaxHealth => maxHealth;

Deep dive: an API for values, not just a different field syntax

Section titled “Deep dive: an API for values, not just a different field syntax”

A field is storage; a property is an operation exposed using field-like syntax. get runs code when the value is read, and set runs code when it is assigned. Auto-properties let the compiler create a hidden backing field. A property can expose a value publicly while controlling how it changes, and its implementation can evolve without forcing callers to change player.Health into player.GetHealth().

Program.cs
var player = new PlayerHealth(100);
player.TakeDamage(25);
Console.WriteLine(player.Health); // 75
public sealed class PlayerHealth
{
public int Health { get; private set; }
public PlayerHealth(int startingHealth)
=> Health = Math.Max(0, startingHealth);
public void TakeDamage(int amount)
{
if (amount < 0) throw new ArgumentOutOfRangeException(nameof(amount));
Health = Math.Max(0, Health - amount);
}
}

The caller may read Health but cannot write player.Health = -10 because the setter is private. An explicit method expresses a meaningful game action rather than allowing arbitrary state changes.

Properties can compute values, but avoid surprises

Section titled “Properties can compute values, but avoid surprises”

A property such as public bool IsDead => Health == 0; calculates a result each time it is read. A property getter should usually be inexpensive and unsurprising; do not hide network requests or destructive side effects behind what looks like a normal value. Use a method for substantial work or actions with side effects.

Understand set, init, required, and read-only properties

Section titled “Understand set, init, required, and read-only properties”
  • get; set; allows a caller to read and assign a property (subject to accessibility).
  • get; private set; allows public reads but limits assignments to the containing type.
  • get; init; allows assignment during object initialization and selected construction contexts, then prevents ordinary reassignment.
  • get; with a constructor assignment provides a read-only property after construction.
  • required tells callers that a property or field must be initialized, subject to constructors marked with the relevant C# rules.

init and readonly are not interchangeable: init affects a property setter’s allowed calls; readonly on a field restricts when the field may be assigned.

Unity serialization is a separate mechanism

Section titled “Unity serialization is a separate mechanism”

Unity’s built-in Inspector normally serializes supported fields, not ordinary C# auto-properties. For an editable private setting, use [SerializeField] private int maxHealth = 100; and expose a public read-only property where needed. Also validate values at runtime; Inspector attributes such as [Range] do not enforce invariants against assignments made in code.

Expose the minimum interface callers need and perform state changes through meaningful methods or validated setters.

A public getter does not imply a public setter. init is not the same as const or readonly, and properties are not serialized like Unity fields.

Create an Experience property that cannot be changed from outside the class but can increase through an Earn method.

Reveal a solution

public int Experience { get; private set; } plus public void Earn(int amount) { ... }.