Kom i gang med klasseegenskaber og accessors

Fuldført

Klasseegenskaber giver en fleksibel mekanisme til at læse, skrive eller beregne værdien af et datafelt. De vises som offentlige datamedlemmer, men de implementeres som særlige metoder, der kaldes accessors. Denne funktion gør det nemt for opkaldere at få adgang til data og hjælper stadig med at fremme datasikkerhed og fleksibilitet.

Brug af egenskaber

Egenskaber kombinerer aspekter af både felter og metoder. For brugeren af et objekt er en egenskab tilsyneladende et felt. Adgang til egenskaben kræver samme syntaks som adgang til et felt. For implementereren af en klasse er en egenskab en eller to kodeblokke, der repræsenterer en get accessor og/eller en set eller init accessor. Kodeblokken for den get accessor udføres, når egenskaben læses. Kodeblokken for den set eller init accessor udføres, når egenskaben tildeles en værdi. En egenskab uden en set accessor betragtes som skrivebeskyttet. En egenskab uden en get accessor betragtes som skrivebeskyttet. En egenskab, der har begge accessors, er læse-/skriveadgang. Du kan bruge en init accessor i stedet for en set accessor til at aktivere egenskaben, der skal angives som en del af initialiseringen af objektet, men ellers gøre den skrivebeskyttet.

I modsætning til felter klassificeres egenskaber ikke som variabler. Derfor kan du ikke overføre en egenskab som en ref eller out parameter.

Egenskaber bruges ofte til at understøtte følgende scenarier:

  • De kan validere data, før de tillader en ændring.
  • De kan på gennemsigtig vis vise data i en klasse, hvor disse data hentes fra en anden kilde, f.eks. en database.
  • De kan udføre en handling, når data ændres, f.eks. hæve en hændelse eller ændre værdien af andre felter.

Egenskaber erklæres i kodeblokken i en klassedefinition. Egenskaber erklæres ved at angive adgangsniveauet for feltet efterfulgt af egenskabens type efterfulgt af navnet på egenskaben efterfulgt af en kodeblok, der deklarerer en get accessor og/eller en set accessor.

Den get accessor

Brødteksten i den get accessor ligner den i en metode. Den skal returnere en værdi af egenskabstypen. C#-compileren og JIT-compileren (Just-in-time) registrerer almindelige mønstre til implementering af get-accessoren og optimerer til disse mønstre.

En get accessor, der returnerer et felt uden at udføre nogen beregning, optimeres sandsynligvis til en hukommelseslæsning af det pågældende felt. Automatisk implementerede egenskaber, der undersøges i det næste undermodul, følger dette mønster og drager fordel af disse optimeringer. En virtuel get accessormetode kan dog ikke indsættes, fordi compileren ikke ved på kompileringstidspunktet, hvilken metode der faktisk kaldes på kørselstidspunktet.

I følgende eksempel vises en get accessor, der returnerer værdien af et privat felt _name:


public class Employee
{
    private string _name = "unknown";  // the name field
    public string Name
    {
        get { return _name; }  // the Name property
    }
}

I dette eksempel indeholder klassen Employee et privat felt og en offentlig egenskab. Her er en forklaring på definitionen af Employee klasse:

  • Klassen Employee er defineret med modifikatoren til offentlig adgang, hvilket betyder, at den er tilgængelig fra enhver anden kode i den samme assembly eller en anden assembly, der refererer til den.
  • Det private felt _name erklæres med typen string og tildeles en standardværdi "unknown".
  • Den offentlige egenskab Name erklæres med typen string. Egenskaben indeholder en get accessor, der returnerer værdien af feltet _name.

Seddel

De felter, der leverer værdierne for egenskabsadgangsfaktorer, kaldes ofte backing fields.

Brug et øjeblik på at overveje den kode, der instantierer et Employee-objekt, og læser værdien af egenskaben Name:

Employee employee = new Employee();  // create an instance of the Employee class

Console.Write(employee.Name);  // use the get accessor to read the value of the Name property

Dette kodeeksempel instantierer et nyt Employee objekt med navnet employee. Når din kode refererer til egenskaben employee.Name, undtagen som destination for en tildeling, aktiveres den get accessor for at læse værdien af egenskaben. I dette tilfælde returnerer den get accessor værdien af feltet _name, som tildeles værdien "unknown".

Kodeblokken for en get accessor kan også bruges til at returnere en beregnet værdi. Dette kan være nyttigt, når du vil sikre, at en egenskab altid returnerer en værdi, der ikke er null. For eksempel:


public class Manager
{
    private string? _name;
    public string Name
    {
        get
        {
            return _name != null ? _name : "NA";
        }
    }
}

Den set accessor

Den set accessor ligner en metode, hvis returtype er ugyldig. Den bruger en implicit parameter kaldet value, hvis type er egenskabens type. Compileren og JIT-compileren genkender også almindelige mønstre for en set eller init accessor. Disse almindelige mønstre er optimeret og skriver direkte hukommelsen til backing-feltet. I følgende eksempel føjes der en set-accessor til egenskaben Name:


class Student
{
    private string? _name;  // the name field
    public string Name    // the Name property
    {
        get
        {
            return _name != null ? _name : "NA";
        }
        set
        {
            _name = value;
        }
    }
}

Brug et øjeblik på at overveje den kode, der opretter en forekomst af klassen Student. Når du tildeler en værdi til egenskaben Name, aktiveres den set accessor ved hjælp af et argument, der leverer den nye værdi. For eksempel:


var student = new Student();
student.Name = "StudentName";  // the set accessor is invoked here

Console.Write(student.Name);  // the get accessor is invoked here

Den init accessor

Den kode, der bruges til at oprette en init accessor, er den samme som den kode, der bruges til at oprette en set accessor, bortset fra at du bruger nøgleordet init i stedet for set. Forskellen er, at den init accessor kun kan bruges i konstruktøren eller ved hjælp af en object-initializer.

Deklarer og brug læse-/skriveegenskaber

Egenskaber gør det praktisk for offentlige datamedlemmer uden de risici, der følger med ubeskyttet, ukontrolleret og ikke-bekræftet adgang til et objekts data. Egenskaber deklarerer accessors: særlige metoder, der tildeler og henter værdier fra det underliggende datamedlem. Den set accessor gør det muligt at tildele datamedlemmer, og den get accessor henter datamedlemsværdier.

Dette eksempel viser en Person klasse, der har to egenskaber: Name (streng) og Age (int). Begge egenskaber giver get og set accessors, så de betragtes som læse-/skriveegenskaber.


class Person
{
    private string _name = "N/A";
    private int _age = 0;

    // Declare a Name property of type string:
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
        }
    }

    // Declare an Age property of type int:
    public int Age
    {
        get
        {
            return _age;
        }
        set
        {
            _age = value;
        }
    }
}

class TestPerson
{
    static void Main()
    {
        // Create a new Person object named person:
        Person person = new Person();

        // Print out the default name and age of the person:
        Console.WriteLine($"Person details - Name = {person.Name}, Age = {person.Age}");

        // Set some values on the person object:
        person.Name = "PersonName";
        person.Age = 99;
        Console.WriteLine($"Person details - Name = {person.Name}, Age = {person.Age}");

        // Increment the Age property:
        person.Age += 1;
        Console.WriteLine($"Person details - Name = {person.Name}, Age = {person.Age}");

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}

/* Output:
Person details - Name = N/A, Age = 0
Person details - Name = PersonName, Age = 99
Person details - Name = PersonName, Age = 100
*/

Accessorsyntaks og kodningsteknikker

Ud over den grundlæggende syntaks for erklæring af egenskaber indeholder C# syntaks, der gør det muligt at skrive mere præcis og udtryksfuld kode. Disse funktioner omfatter:

  • Udtryks-bodied-medlemmer
  • Feltunderstøttede egenskaber
  • Obligatoriske egenskaber

Udtryks-bodied-medlemmer

Medlemmer med udtryk giver en mere præcis syntaks til skrivning af enkeltlinjeegenskabsadgange.

Seddel

Udtryksaktiverede medlemmer kan også anvendes på metoder, indekseringsprogrammer og hændelsesadgangsbrugere.

Egenskabsadgangsbrugere består ofte af enkeltlinjesætninger, der tildeler eller returnerer resultatet af et udtryk. Den Person klasse, du undersøgte tidligere i dette undermodul, er et godt eksempel:


class Student
{
    private string? _name;  // the name field
    public string Name    // the Name property
    {
        get
        {
            return _name != null ? _name : "NA";
        }
        set
        {
            _name = value;
        }
    }
}

I dette eksempel bruger egenskaben Name en enkeltlinjesætning til at returnere værdien af feltet _name, hvis det ikke er null, eller strengen "NA", hvis den er null. Den set accessor bruger en enkeltlinjesætning til at tildele værdien af egenskaben Name til feltet _name.

En brødtekstdefinition for udtryk består af det => token efterfulgt af det udtryk, der bruges til at tildele eller hente egenskabsværdien. Da de accessors, der er defineret for klassen Student, implementerer en enkeltlinjesætning, er det en god kandidat til opdatering ved hjælp af definitioner af udtryksbrødtekst.

Følgende kodestykke opdaterer den Student klasse ved hjælp af brødtekstdefinitioner for get og set accessors:


class Student
{
    private string? _name;  // the name field
    public string Name    // the Name property
    {
        get => _name ?? "NA";
        set => _name = value;
    }
}

Feltunderstøttede egenskaber

Fra og med C# 13 kan du tilføje validering eller anden logik i accessoren for en egenskab ved hjælp af funktionen field nøgleordseksempel. Nøgleordet field tilgår compilerens syntetiserede backing-felt for en egenskab. Den giver dig mulighed for at skrive en egenskabsadgang uden eksplicit at erklære et separat bagsidefelt.


public class Person
{
    public string? FirstName 
    { 
        get;
        set => field = value.Trim(); 
    }

    // Omitted for brevity.
}

Vigtig

Nøgleordet field er en prøveversionsfunktion i C# 13. Du skal bruge .NET 9 og angive det <LangVersion> element til eksempelvisning i projektfilen for at kunne bruge det kontekstafhængige nøgleord field.

Du skal være forsigtig med at bruge nøgleordsfunktionen field i en klasse, der har et felt med navnet field. Det nye field nøgleord skygger et felt med navnet field i området for en egenskabsadgang. Du kan enten ændre navnet på variablen field eller bruge tokenet @ til at referere til felt-id'et som @field.

Obligatoriske egenskaber

I det foregående eksempel kan en kalder oprette en Person ved hjælp af standardkonstruktøren uden at angive egenskaben FirstName. Egenskabstypen er angivet til en streng, der kan være null. Fra og med C# 11 kan du kræve, at opkaldere angiver en egenskab:


public class Person
{
    public Person() { }

    [SetsRequiredMembers]
    public Person(string firstName) => FirstName = firstName;

    public required string FirstName { get; init; }

    // Omitted for brevity.
}

Den foregående kode foretager to ændringer af klassen Person. For det første indeholder FirstName egenskabserklæringen required modifikator. Det betyder, at enhver kode, der opretter en ny Person skal angive denne egenskab ved hjælp af en objektinitialiseringsfunktion. For det andet har den konstruktør, der tager en firstName-parameter, attributten System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute. Denne attribut informerer compileren om, at denne konstruktør angiver alle påkrævede medlemmer. Opkald, der bruger denne konstruktør, er ikke påkrævet for at angive required egenskaber med en objektinitialiseringsfunktion.

Vigtig

Forveksler ikke required med ikke-nullable. Det er gyldigt at angive en required-egenskab til null eller default. Hvis typen ikke kan være null, f.eks. string i disse eksempler, udsteder compileren en advarsel.


var aPerson = new Person("PersonName");
aPerson = new Person{ FirstName = "PersonName"};
// Error CS9035: Required member `Person.FirstName` must be set:
//aPerson2 = new Person();