Eigenschappen gebruiken (C#-programmeerhandleiding)
Eigenschappen combineren aspecten van zowel velden als methoden. Voor de gebruiker van een object lijkt een eigenschap een veld te zijn; Voor toegang tot de eigenschap is dezelfde syntaxis vereist. Voor de implementator van een klasse is een eigenschap één of twee codeblokken, die een get
-accessor en/of een set
- of init
-accessor voorstellen. Het codeblok voor de get
accessor wordt uitgevoerd wanneer de eigenschap wordt gelezen. Het codeblok voor de set
of init
accessor wordt uitgevoerd wanneer aan de eigenschap een waarde wordt toegewezen. Een eigenschap zonder set
accessor wordt beschouwd als alleen voor lezen. Een eigenschap zonder een get
accessor wordt beschouwd als alleen-schrijfbaar. Een eigenschap met beide accessors is lezen/schrijven. U kunt een init
accessor gebruiken in plaats van een set
accessor om de eigenschap in te stellen als onderdeel van objectinitialisatie, maar verder alleen-lezen te maken.
In tegenstelling tot velden worden eigenschappen niet geclassificeerd als variabelen. Daarom kunt u een eigenschap niet doorgeven als een ref
of out
parameter.
Eigenschappen hebben veel gebruik:
- Ze kunnen gegevens valideren voordat ze een wijziging toestaan.
- Ze kunnen transparant gegevens beschikbaar maken in een klasse waarin die gegevens worden opgehaald uit een andere bron, zoals een database.
- Ze kunnen actie ondernemen wanneer gegevens worden gewijzigd, zoals het genereren van een gebeurtenis of het wijzigen van de waarde van andere velden.
Eigenschappen worden gedeclareerd in het klasseblok door het toegangsniveau van het veld op te geven, gevolgd door het type eigenschap, gevolgd door de naam van de eigenschap en gevolgd door een codeblok dat een get
-accessor en/of een set
accessor declareert. Voorbeeld:
public class Date
{
private int _month = 7; // Backing store
public int Month
{
get => _month;
set
{
if ((value > 0) && (value < 13))
{
_month = value;
}
}
}
}
In dit voorbeeld wordt Month
gedeclareerd als een eigenschap, zodat de set
accessor ervoor kan zorgen dat de Month
waarde tussen 1 en 12 wordt ingesteld. De Month
eigenschap maakt gebruik van een privéveld om de werkelijke waarde bij te houden. De werkelijke locatie van de gegevens van een eigenschap wordt vaak 'backing store' van de eigenschap genoemd. Het is gebruikelijk dat eigenschappen privévelden gebruiken als back-uparchief. Het veld is gemarkeerd als privé om ervoor te zorgen dat het alleen kan worden gewijzigd door de eigenschap aan te roepen. Zie Toegangsmodifiers voor meer informatie over beperkingen voor openbare en persoonlijke toegang. Automatisch geïmplementeerde eigenschappen bieden vereenvoudigde syntaxis voor eenvoudige eigenschapsdeclaraties. Zie Eigenschappen automatisch geïmplementeerd voor meer informatie.
Vanaf C# 13 kunt u eigenschappen met veldsteun gebruiken om validatie toe te voegen aan de set
toegangsfunctie van een automatisch geïmplementeerde eigenschap, zoals wordt weergegeven in het volgende voorbeeld:
public class DateExample
{
public int Month
{
get;
set
{
if ((value > 0) && (value < 13))
{
field = value;
}
}
}
}
Belangrijk
Het field
trefwoord is een preview-functie in C# 13. U moet .NET 9 gebruiken en het <LangVersion>
element preview
instellen op in uw projectbestand om het field
contextuele trefwoord te kunnen gebruiken.
Wees voorzichtig met het gebruik van de trefwoordfunctie in een klasse met een veld met de field
naam field
. Het nieuwe field
trefwoord schaduwt een veld met de naam field
in de context van een eigenschapstoegangsfunctie. U kunt de naam van de field
variabele wijzigen of het @
token gebruiken om naar de field
id te verwijzen.@field
Meer informatie vindt u door de functiespecificatie voor het field
trefwoord te lezen.
De get-accessor
De hoofdtekst van de get
accessor heeft overeenkomst met die van een methode. Er moet een waarde van het eigenschapstype worden geretourneerd. De C#-compiler en JIT-compiler (Just-In-Time) detecteren veelvoorkomende patronen voor het implementeren van de get
accessor en optimaliseert deze patronen. Een get
accessor die bijvoorbeeld een veld retourneert zonder berekeningen uit te voeren, is waarschijnlijk geoptimaliseerd voor een geheugenleesbewerking van dat veld. Automatisch geïmplementeerde eigenschappen volgen dit patroon en profiteren van deze optimalisaties. Een virtuele get
accessormethode kan echter niet inline worden geplaatst omdat de compiler niet weet op het moment van compileren welke methode daadwerkelijk kan worden aangeroepen tijdens runtime. In het volgende voorbeeld ziet u een get
accessor die de waarde van een privéveld _name
retourneert:
class Employee
{
private string _name; // the name field
public string Name => _name; // the Name property
}
Wanneer u naar de eigenschap verwijst, met uitzondering van het doel van een toewijzingsoperatie, wordt de get
accesseermethode aangeroepen om de waarde van de eigenschap te lezen. Voorbeeld:
var employee = new Employee();
//...
System.Console.Write(employee.Name); // the get accessor is invoked here
De get
toegangsfunctie moet een expressie-leden zijn, of eindigen op een return- of throw-instructie, en de besturing kan niet buiten de body van de toegangsfunctie komen.
Waarschuwing
Het is over het algemeen een slechte programmeerstijl om de status van het object te wijzigen met behulp van de get
accessor. Een uitzondering op deze regel is een eigenschap met uitgestelde evaluatie, waarbij de waarde van een eigenschap alleen wordt berekend wanneer deze voor het eerst wordt opgevraagd.
De get
accessor kan worden gebruikt om de veldwaarde te retourneren of om deze te berekenen en te retourneren. Voorbeeld:
class Manager
{
private string _name;
public string Name => _name != null ? _name : "NA";
}
Als u in het vorige voorbeeld geen waarde aan de Name
eigenschap toewijst, wordt de waarde NA
geretourneerd.
De set accessor
De set
toegangsfunctie lijkt op een methode waarvan het retourtype void is. Hierbij wordt een impliciete parameter met de naam value
gebruikt, waarvan het type het type van de eigenschap is. De compiler en JIT-compiler herkennen ook veelvoorkomende patronen voor een set
of init
accessor. Deze algemene patronen zijn geoptimaliseerd, waarbij het geheugen voor het back-upveld rechtstreeks wordt geschreven. In het volgende voorbeeld wordt een set
toegangsfunctie toegevoegd aan de Name
eigenschap:
class Student
{
private string _name; // the name field
public string Name // the Name property
{
get => _name;
set => _name = value;
}
}
Wanneer u een waarde aan de eigenschap toewijst, wordt de set
accessor aangeroepen met behulp van een argument dat de nieuwe waarde levert. Voorbeeld:
var student = new Student();
student.Name = "Joe"; // the set accessor is invoked here
System.Console.Write(student.Name); // the get accessor is invoked here
Het is een fout om de impliciete parameternaam value
te gebruiken voor een lokale variabeledeclaratie in een set
accessor.
De init-toegangsmethode
De code voor het maken van een init
accessor is hetzelfde als de code voor het maken van een set
accessor, behalve dat u het init
trefwoord gebruikt in plaats van set
. Het verschil is dat de init
accessor alleen kan worden gebruikt in de constructor of met behulp van een object-initializer.
Opmerkingen
Eigenschappen kunnen worden gemarkeerd als public
, private
, protected
, internal
, , protected internal
of private protected
. Deze toegangsaanpassingen definiëren hoe gebruikers van de klasse toegang hebben tot de eigenschap. De get
en set
accessors voor dezelfde eigenschap kunnen verschillende toegangsmodificaties hebben. Bijvoorbeeld, de get
kan zo worden ingesteld dat alleen lezen toegang van buiten het type is toegestaan, en de public
kan set
, private
of protected
zijn. Zie Toegangsmodifiers voor meer informatie.
Een eigenschap kan worden gedeclareerd als een statische eigenschap met behulp van het static
trefwoord. Statische eigenschappen zijn op elk gewenst moment beschikbaar voor bellers, zelfs als er geen exemplaar van de klasse bestaat. Zie Statische klassen en statische klasseleden voor meer informatie.
Een eigenschap kan worden gemarkeerd als een virtuele eigenschap met behulp van het virtuele trefwoord. Met virtuele eigenschappen kunnen afgeleide klassen het gedrag van de eigenschap overschrijven met behulp van het trefwoord overschrijven . Zie Overname voor meer informatie over deze opties.
Een eigenschap die een virtuele eigenschap overschrijft, kan ook worden verzegeld, wat aangeeft dat deze niet meer virtueel is voor afgeleide klassen. Ten slotte kan een eigenschap worden gedeclareerd als abstract. Abstracte eigenschappen definiëren geen implementatie in de klasse en afgeleide klassen moeten hun eigen implementatie schrijven. Zie Abstracte en verzegelde klassen en klasleden voor meer informatie over deze opties.
Notitie
Het is een fout om een virtuele, abstracte of override modifier te gebruiken bij een accessor van een statische eigenschap.
Voorbeelden
Dit voorbeeld illustreert instanties, statische en alleen-lezen eigenschappen. Het accepteert de naam van de werknemer van het toetsenbord, verhoogt NumberOfEmployees
met 1, en geeft de naam en het nummer van de werknemer weer.
public class Employee
{
public static int NumberOfEmployees;
private static int _counter;
private string _name;
// A read-write instance property:
public string Name
{
get => _name;
set => _name = value;
}
// A read-only static property:
public static int Counter => _counter;
// A Constructor:
public Employee() => _counter = ++NumberOfEmployees; // Calculate the employee's number:
}
Voorbeeld van verborgen eigenschap
In dit voorbeeld ziet u hoe u toegang krijgt tot een eigenschap in een basisklasse die wordt verborgen door een andere eigenschap met dezelfde naam in een afgeleide klasse:
public class Employee
{
private string _name;
public string Name
{
get => _name;
set => _name = value;
}
}
public class Manager : Employee
{
private string _name;
// Notice the use of the new modifier:
public new string Name
{
get => _name;
set => _name = value + ", Manager";
}
}
class TestHiding
{
public static void Test()
{
Manager m1 = new Manager();
// Derived class property.
m1.Name = "John";
// Base class property.
((Employee)m1).Name = "Mary";
System.Console.WriteLine($"Name in the derived class is: {m1.Name}");
System.Console.WriteLine($"Name in the base class is: {((Employee)m1).Name}");
}
}
/* Output:
Name in the derived class is: John, Manager
Name in the base class is: Mary
*/
Dit zijn belangrijke punten in het vorige voorbeeld:
- De eigenschap
Name
in de afgeleide klasse verbergt de eigenschapName
in de basisklasse. In dat geval wordt denew
wijzigingsfunctie gebruikt in de declaratie van de eigenschap in de afgeleide klasse:public new string Name
- De cast
(Employee)
wordt gebruikt voor toegang tot de verborgen eigenschap in de basisklasse:((Employee)m1).Name = "Mary";
Zie de nieuwe modifier voor meer informatie over het verbergen van leden.
Voorbeeld van overschrijvingseigenschap
In dit voorbeeld implementeren twee klassen, Cube
en Square
, een abstracte klasse, Shape
, en overschrijven ze de abstracte eigenschap Area
. Let op het gebruik van de override-modificator bij de eigenschappen. Het programma accepteert de zijkant als invoer en berekent de gebieden voor het vierkant en de kubus. Het accepteert ook de oppervlakte als invoer en berekent de bijbehorende zijde van het vierkant en de kubus.
abstract class Shape
{
public abstract double Area
{
get;
set;
}
}
class Square : Shape
{
public double side;
//constructor
public Square(double s) => side = s;
public override double Area
{
get => side * side;
set => side = System.Math.Sqrt(value);
}
}
class Cube : Shape
{
public double side;
//constructor
public Cube(double s) => side = s;
public override double Area
{
get => 6 * side * side;
set => side = System.Math.Sqrt(value / 6);
}
}
class TestShapes
{
static void Main()
{
// Input the side:
System.Console.Write("Enter the side: ");
double side = double.Parse(System.Console.ReadLine());
// Compute the areas:
Square s = new Square(side);
Cube c = new Cube(side);
// Display the results:
System.Console.WriteLine($"Area of the square = {s.Area:F2}");
System.Console.WriteLine($"Area of the cube = {c.Area:F2}");
System.Console.WriteLine();
// Input the area:
System.Console.Write("Enter the area: ");
double area = double.Parse(System.Console.ReadLine());
// Compute the sides:
s.Area = area;
c.Area = area;
// Display the results:
System.Console.WriteLine($"Side of the square = {s.side:F2}");
System.Console.WriteLine($"Side of the cube = {c.side:F2}");
}
}
/* Example Output:
Enter the side: 4
Area of the square = 16.00
Area of the cube = 96.00
Enter the area: 24
Side of the square = 4.90
Side of the cube = 2.00
*/