Undersøg automatisk implementerede egenskaber
Standardsyntaksen for definition af en egenskab i C# indeholder en get accessor og en set accessor. Men når der ikke kræves anden logik i accessorerne, kan du bruge automatisk implementerede egenskaber.
Automatisk implementerede egenskaber
Automatisk implementerede egenskaber gør egenskabserklæringen mere præcis, når der ikke kræves anden logik i egenskabsadgangslederne. De gør det også muligt for klientkoden at oprette objekter. Når du deklarerer en egenskab som vist i følgende eksempel, opretter compileren et privat anonymt backing-felt, der kun kan åbnes via egenskabens get og set accessors.
init accessors kan også erklæres som automatisk implementerede egenskaber.
I følgende eksempel vises en simpel klasse, der har nogle automatisk implementerede egenskaber:
// This class is mutable. Its data can be modified from
// outside the class.
public class Customer
{
// Auto-implemented properties for trivial get and set
public double TotalPurchases { get; set; }
public string Name { get; set; }
public int CustomerId { get; set; }
// Constructor
public Customer(double purchases, string name, int id)
{
TotalPurchases = purchases;
Name = name;
CustomerId = id;
}
// Methods
public string GetContactInfo() { return "ContactInfo"; }
public string GetTransactionHistory() { return "History"; }
// .. Other methods, events, etc.
}
class Program
{
static void Main()
{
// Initialize a new object.
Customer cust1 = new Customer(4987.63, "Northwind", 90108);
// Modify a property.
cust1.TotalPurchases += 499.99;
}
}
Automatisk implementerede egenskaber og feltunderstøttede egenskaber deklarerer et privat sikkerhedskopieringsfelt. Du kan initialisere automatisk implementerede egenskaber på samme måde som felter:
public string FirstName { get; set; } = "FirstName";
Den Customer klasse, der blev vist i det forrige eksempel, kan slås fra. Klientkoden kan ændre værdierne i objekter efter oprettelsen. I komplekse klasser, der indeholder betydelig funktionsmåde (metoder) og data, er det ofte nødvendigt at have offentlige egenskaber. Men for små klasser eller strukturer, der blot indkapsler et værdisæt (data) og har lidt eller ingen funktionsmåder, skal du bruge en af følgende muligheder for at gøre objekterne uforanderlige:
- Deklarer kun en hent-accessor (uforanderlig overalt undtagen konstruktøren).
- Deklarer en get accessor og en init accessor (uforanderlig overalt undtagen under objektkonstruktion).
- Deklarer den indstillede accessor som privat (uforanderlig for forbrugere).
Du skal muligvis føje validering til en automatisk implementeret egenskab. C# 13 tilføjer feltunderstøttede egenskaber som en prøveversionsfunktion. Du kan bruge feltnøgleordet til at få adgang til compilerens syntetiserede backing-felt for en automatisk implementeret egenskab. Du kan f.eks. sikre, at egenskaben FirstName i det foregående eksempel ikke kan angives til null eller den tomme streng:
public string FirstName
{
get;
set
{
field = (string.IsNullOrWhiteSpace(value) is false
? value
: throw new ArgumentException(nameof(value), "First name can't be whitespace or null"));
}
} = "FirstName";
Denne funktion giver dig mulighed for at føje logik til accessors, uden at du udtrykkeligt skal deklarere opbakningsfeltet. Du kan bruge feltnøgleordet til at få adgang til det sikkerhedskopieringsfelt, der genereres af compileren.
Vigtig
Nøgleordet field er en prøveversionsfunktion i C# 13. Du skal bruge .NET 9 og angive dit <LangVersion>-element til at preview 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 field-id'et som @field.
Egenskaber med baggrundsfelter
Du kan blande begrebet beregnet egenskab med et privat felt og oprette en cachelagret evalueret egenskab. Opdater f.eks. egenskaben FullName, så strengformateringen sker ved den første adgang:
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
Denne implementering fungerer, fordi egenskaberne FirstName og LastName er skrivebeskyttede. Folk kan ændre deres navn. Hvis du opdaterer egenskaberne for FirstName og LastName for at tillade set adgangsbrugere, skal du annullere alle cachelagrede værdier for fullName. Du ændrer accessorerne set for og-egenskaben FirstNameLastName , så fullName feltet beregnes igen:
public class Person
{
private string? _firstName;
public string? FirstName
{
get => _firstName;
set
{
_firstName = value;
_fullName = null;
}
}
private string? _lastName;
public string? LastName
{
get => _lastName;
set
{
_lastName = value;
_fullName = null;
}
}
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
Denne endelige version evaluerer kun egenskaben FullName, når det er nødvendigt. Den tidligere beregnede version bruges, hvis den er gyldig. Ellers opdaterer beregningen den cachelagrede værdi. Udviklere, der bruger denne klasse, behøver ikke at kende detaljerne for implementeringen. Ingen af disse interne ændringer påvirker brugen af objektet Person.
Fra og med C# 13 kan du oprette partial properties i delvise klasser. Gennemførelseserklæringen for en partial egenskab kan ikke være en automatisk implementeret egenskab. En automatisk implementeret egenskab bruger samme syntaks som en erklæring om delvis egenskabserklæring.
Implementer en letvægtsklasse med automatisk implementerede egenskaber
Du kan støde på situationer, hvor du har brug for at oprette en uforanderlig letvægtsklasse, der kun bruges til at indkapsle et sæt automatisk implementerede egenskaber. Brug denne type konstruktion i stedet for en struct, når du skal bruge semantik af referencetypen.
Du kan gøre en uforanderlig egenskab på følgende måder:
- Deklarer kun den
getaccessor, hvilket gør egenskaben uforanderlig overalt undtagen i typens konstruktør. - Deklarer en
initaccessor i stedet for ensetaccessor, hvilket gør egenskaben indstillelig kun i konstruktøren eller ved hjælp af en objektinitialisering. - Deklarer, at
set-adgangsvælgeren skalprivate. Egenskaben kan angives i typen , men den er uforanderlig for forbrugere.
Du kan føje required modifikator til egenskabserklæringen for at tvinge kaldere til at angive egenskaben som en del af initialiseringen af et nyt objekt.
I følgende eksempel kan du se, hvordan en egenskab med kun get accessor adskiller sig fra én med get og private set.
class Contact
{
public string Name { get; }
public string Address { get; private set; }
public Contact(string contactName, string contactAddress)
{
// Both properties are accessible in the constructor.
Name = contactName;
Address = contactAddress;
}
// Name isn't assignable here. This will generate a compile error.
//public void ChangeName(string newName) => Name = newName;
// Address is assignable here.
public void ChangeAddress(string newAddress) => Address = newAddress;
}
I følgende eksempel vises to måder at implementere en uforanderlig klasse på ved hjælp af automatisk implementerede egenskaber. Hver vej deklarerer en af egenskaberne med en privat set og en af egenskaberne med kun en get. Den første klasse bruger kun en konstruktør til at initialisere egenskaberne, og den anden klasse bruger en statisk fabriksmetode, der kalder en konstruktør.
// This class is immutable. After an object is created,
// it can't be modified from outside the class. It uses a
// constructor to initialize its properties.
class Contact
{
// Read-only property.
public string Name { get; }
// Read-write property with a private set accessor.
public string Address { get; private set; }
// Public constructor.
public Contact(string contactName, string contactAddress)
{
Name = contactName;
Address = contactAddress;
}
}
// This class is immutable. After an object is created,
// it can't be modified from outside the class. It uses a
// static method and private constructor to initialize its properties.
public class Contact2
{
// Read-write property with a private set accessor.
public string Name { get; private set; }
// Read-only property.
public string Address { get; }
// Private constructor.
private Contact2(string contactName, string contactAddress)
{
Name = contactName;
Address = contactAddress;
}
// Public factory method.
public static Contact2 CreateContact(string name, string address)
{
return new Contact2(name, address);
}
}
public class Program
{
static void Main()
{
// Some simple data sources.
string[] names = ["Person One","Person Two", "Person Three",
"Person Four", "Person Five"];
string[] addresses = ["123 Main St.", "345 Cypress Ave.", "678 1st Ave",
"12 108th St.", "89 E. 42nd St."];
// Simple query to demonstrate object creation in select clause.
// Create Contact objects by using a constructor.
var query1 = from i in Enumerable.Range(0, 5)
select new Contact(names[i], addresses[i]);
// List elements can't be modified by client code.
var list = query1.ToList();
foreach (var contact in list)
{
Console.WriteLine("{0}, {1}", contact.Name, contact.Address);
}
// Create Contact2 objects by using a static factory method.
var query2 = from i in Enumerable.Range(0, 5)
select Contact2.CreateContact(names[i], addresses[i]);
// Console output is identical to query1.
var list2 = query2.ToList();
// List elements can't be modified by client code.
// CS0272:
// list2[0].Name = "Person Six";
}
}
/* Output:
Person One, 123 Main St.
Person Two, 345 Cypress Ave.
Person Three, 678 1st Ave
Person Four, 12 108th St.
Person Five, 89 E. 42nd St.
*/
Compileren opretter sikkerhedskopieringsfelter for hver automatisk implementerede egenskab. Felterne er ikke tilgængelige direkte fra kildekoden.