Hoppa till innehåll

Egenskaper och inkapsling

↑

Exemplen är oberoende C#-utdrag för moderna .NET-konsolprojekt, om inget annat anges. Klassdeklarationer och toppnivåkod kan placeras i samma Program.cs-fil, med toppnivåkoden först.

En egenskap ger kontrollerad åtkomst till data eller beräknade värden. Till skillnad från ett fält kan en egenskap köra logik vid läsning och tilldelning.

Automatiska egenskaper och privata setters

Section titled “Automatiska egenskaper och privata setters”

get läser ett värde och set skriver. Med private set får annan kod läsa egenskapen, men bara den innehållande klassen får ändra den. Det hjälper till att bevara giltigt tillstånd.

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);
}
}

En fullständig egenskap kan använda ett privat bakomliggande fält för att neka ogiltiga värden. Validera tydligt och undvik dyra eller överraskande bieffekter i getters.

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

init låter en egenskap få värde vid objektinitiering men inte senare i vanlig kod. Unitys vanliga serialisering hanterar främst fält, inte vanliga C#-egenskaper; använd ett privat [SerializeField]-fält och en publik läsegenskap för Inspector-data.

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

Fördjupning: egenskaper är ett API för värden

Section titled “Fördjupning: egenskaper är ett API för värden”

Ett fält lagrar data. En egenskap ser ut som ett fält för anroparen, men get och set kan köra kod när värdet läses respektive tilldelas. För en automatisk egenskap skapar kompilatorn ett dolt bakomliggande fält. Du kan ge offentlig läsbehörighet och ändå kontrollera hur tillståndet ändras. Implementation kan senare ändras utan att alla anrop till player.Health måste skrivas om till 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);
}
}

Anroparen kan läsa Health men kan inte skriva player.Health = -10, eftersom set-accessorn är privat. En metod som TakeDamage beskriver en spelhandling och tillåter validering innan tillståndet ändras.

Beräknade egenskaper ska vara förutsägbara

Section titled “Beräknade egenskaper ska vara förutsägbara”

public bool IsDead => Health == 0; räknar fram ett booleskt värde varje gång det läses. En getter bör vanligtvis vara snabb och utan överraskande sidoeffekter. Dölj inte ett nätverksanrop eller en destruktiv operation bakom en egenskap som ser ut att bara returnera data. Använd en metod när något faktiskt görs.

Skillnaden mellan set, init, required och skrivskyddade egenskaper

Section titled “Skillnaden mellan set, init, required och skrivskyddade egenskaper”
  • get; set; låter tillåtna anropare både läsa och tilldela värdet.
  • get; private set; ger publik läsning men begränsar tilldelning till den innehållande typen.
  • get; init; tillåter initiering när objektet skapas och vissa konstruktionssammanhang, men inte normal omtilldelning senare.
  • Enbart get; kan tilldelas i konstruktorn och sedan läsas.
  • required kräver att ett fält eller en egenskap initieras av den som skapar objektet, med de undantag som språket tillåter för konstruktorer.

init och readonly betyder olika saker: init gäller när en egenskaps setter får anropas; readonly på ett fält gäller när fältet får tilldelas.

Unitys vanliga Inspector serialiserar fält som stöds, inte vanliga automatiska C#-egenskaper. Använd till exempel [SerializeField] private int maxHealth = 100; när designern ska kunna ändra ett privat fält, och exponera en publik skrivskyddad egenskap vid behov. Validera dessutom värden under körning. Inspector-attribut som [Range] skyddar inte mot felaktiga tilldelningar direkt i kod.

Exponera endast den åtkomst anropare behöver och låt kontrollerade metoder eller setters ändra tillstånd.

En publik getter innebär inte en publik setter. init är inte samma sak som const eller readonly, och egenskaper serialiseras inte som vanliga Unity-fält.

Skapa en Experience-egenskap som bara går att ändra genom klassens metod Earn.

Visa ett lösningsförslag

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