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 implementeerfunctie van een klasse is een eigenschap een of twee codeblokken, die een get
accessor en/of een set
of init
accessor vertegenwoordigen. 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 accessor set
wordt beschouwd als alleen-lezen. Een eigenschap zonder get
een accessor wordt beschouwd als alleen-schrijven. 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 object initialisatie, maar anders 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 Month
wordt deze gedeclareerd als een eigenschap, zodat de set
toegangsfunctie ervoor kan zorgen dat de Month
waarde tussen 1 en 12 is 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.
De get-accessor
De hoofdtekst van de get
accessor lijkt op 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 toewijzing, wordt de get
toegangsservice 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-bodyied lid zijn, of eindigen op een retour - of throw-instructie , en het besturingselement kan niet uit de hoofdtekst van de toegangsfunctie stromen.
Waarschuwing
Het is een slechte programmeerstijl om de status van het object te wijzigen met behulp van de get
accessor.
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 ongeldig 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-toegangsrechten
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 toegangsaanpassingen hebben. Het kan bijvoorbeeld get
zijn dat alleen-lezentoegang van buiten het type is toegestaan, en het set
kan of private
protected
public
. 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 bij het gebruik van een virtuele, abstracte of onderdrukkingsaanpassing voor een toegangsfunctie van een statische eigenschap.
Voorbeelden
In dit voorbeeld ziet u de eigenschappen exemplaar, statisch en alleen-lezen. Hiermee wordt de naam van de werknemer van het toetsenbord geaccepteerd, wordt verhoogd NumberOfEmployees
met 1 en wordt de naam en het nummer van de werknemer weergegeven.
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: {0}", m1.Name);
System.Console.WriteLine("Name in the base class is: {0}", ((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 onderdrukkingseigenschap
In dit voorbeeld implementeren twee klassen Cube
en Square
implementeert u een abstracte klasse Shape
en overschrijft u de abstracte Area
eigenschap. Let op het gebruik van de onderdrukkingsaanpassing op de eigenschappen. Het programma accepteert de zijkant als invoer en berekent de gebieden voor het vierkant en de kubus. Het gebied wordt ook geaccepteerd als invoer en berekent de bijbehorende zijde voor 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 = {0:F2}", s.Area);
System.Console.WriteLine("Area of the cube = {0:F2}", c.Area);
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 = {0:F2}", s.side);
System.Console.WriteLine("Side of the cube = {0:F2}", c.side);
}
}
/* 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
*/