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.
Snabböversikt
Section titled “Snabböversikt”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.
Så fungerar det
Section titled “Så fungerar det”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.
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); }}Bakomliggande fält och validering
Section titled “Bakomliggande fält och validering”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.
public class VolumeSettings{ private int volume; public int Volume { get => volume; set => volume = Math.Clamp(value, 0, 100); }}Init och Unity-fält
Section titled “Init och Unity-fält”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.
// 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”Varför inte bara använda publika fält?
Section titled “Varför inte bara använda publika fält?”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().
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. requiredkrä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 serialisering fungerar annorlunda
Section titled “Unitys serialisering fungerar annorlunda”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.
När och varför?
Section titled “När och varför?”Exponera endast den åtkomst anropare behöver och låt kontrollerade metoder eller setters ändra tillstånd.
Vanligt misstag
Section titled “Vanligt misstag”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.
Testa själv
Section titled “Testa själv”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) { ... }.
