Vlastnosti
Vlastnosti jsou občany první třídy v jazyce C#. Jazyk definuje syntaxi, která vývojářům umožňuje psát kód, který přesně vyjadřuje záměr návrhu.
Vlastnosti se chovají jako pole při přístupu. Na rozdíl od polí jsou však vlastnosti implementovány s přístupovými objekty, které definují příkazy spuštěné při přístupu nebo přiřazení vlastnosti.
Syntaxe vlastností
Syntaxe vlastností je přirozené rozšíření polí. Pole definuje umístění úložiště:
public class Person
{
public string? FirstName;
// Omitted for brevity.
}
Definice vlastnosti obsahuje deklarace pro get
objekt a set
přístup, který načte a přiřadí hodnotu této vlastnosti:
public class Person
{
public string? FirstName { get; set; }
// Omitted for brevity.
}
Syntaxe uvedená výše je syntaxe automatické vlastnosti . Kompilátor vygeneruje umístění úložiště pro pole, které zálohuje vlastnost. Kompilátor také implementuje tělo get
a set
přístupové objekty.
Někdy je nutné inicializovat vlastnost na jinou hodnotu než výchozí pro její typ. Jazyk C# to povolí nastavením hodnoty za pravou závorkou vlastnosti. Místo toho můžete dát přednost počáteční hodnotě vlastnosti FirstName
jako prázdný řetězec, nikoli null
. To byste zadali, jak je znázorněno níže:
public class Person
{
public string FirstName { get; set; } = string.Empty;
// Omitted for brevity.
}
Specifická inicializace je nejužitečnější pro vlastnosti jen pro čtení, jak uvidíte dále v tomto článku.
Úložiště můžete definovat také sami, jak je znázorněno níže:
public class Person
{
public string? FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
private string? _firstName;
// Omitted for brevity.
}
Pokud je implementace vlastnosti jedním výrazem, můžete pro getter nebo setter použít členy s body výrazu:
public class Person
{
public string? FirstName
{
get => _firstName;
set => _firstName = value;
}
private string? _firstName;
// Omitted for brevity.
}
Tato zjednodušená syntaxe se použije tam, kde je to možné v tomto článku.
Výše uvedená definice vlastnosti je vlastnost pro čtení i zápis. Všimněte si klíčového slova value
v přístupovém objektu set. Příslušenství set
má vždy jeden parametr s názvem value
. Přistupovací get
objekt musí vracet hodnotu, která je konvertibilní na typ vlastnosti (string
v tomto příkladu).
To jsou základy syntaxe. Existuje mnoho různých variant, které podporují různé designové idiomy. Pojďme se podívat a seznámit se s možnostmi syntaxe pro každou z nich.
Ověřování
Výše uvedené příklady ukázaly jeden z nejjednodušších případů definice vlastnosti: vlastnost pro čtení i zápis bez ověření. Napsáním požadovaného kódu v objektech get
a set
přístupových objektech můžete vytvořit mnoho různých scénářů.
Do přístupového objektu set
můžete napsat kód, který zajistí, že hodnoty reprezentované vlastností jsou vždy platné. Předpokládejme například, že jedno pravidlo pro Person
třídu znamená, že název nemůže být prázdný nebo prázdný. To byste napsali takto:
public class Person
{
public string? FirstName
{
get => _firstName;
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("First name must not be blank");
_firstName = value;
}
}
private string? _firstName;
// Omitted for brevity.
}
Předchozí příklad lze zjednodušit pomocí výrazu throw
jako součást ověřování setter vlastnosti:
public class Person
{
public string? FirstName
{
get => _firstName;
set => _firstName = (!string.IsNullOrWhiteSpace(value)) ? value : throw new ArgumentException("First name must not be blank");
}
private string? _firstName;
// Omitted for brevity.
}
Výše uvedený příklad vynucuje pravidlo, které nesmí být prázdné nebo prázdné. Pokud vývojář napíše
hero.FirstName = "";
Toto zadání vyvolá výjimku ArgumentException
. Vzhledem k tomu, že přístupové objekty sady vlastností musí mít návratový typ void, hlásíte chyby v přístupovém objektu sady vyvoláním výjimky.
Stejnou syntaxi můžete ve vašem scénáři rozšířit na cokoli potřebného. Můžete zkontrolovat vztahy mezi různými vlastnostmi nebo ověřit vůči jakýmkoli externím podmínkám. Všechny platné příkazy jazyka C# jsou platné v přístupovém objektu vlastnosti.
Řízení přístupu
Do tohoto okamžiku jsou všechny definice vlastností, které jste viděli, vlastnosti čtení a zápisu s veřejnými přístupovými objekty. To není jediná platná přístupnost pro vlastnosti. Můžete vytvořit vlastnosti jen pro čtení nebo nastavit různé přístupnosti pro sadu a získat přístupové objekty. Předpokládejme, že vaše Person
třída by měla povolit pouze změnu hodnoty FirstName
vlastnosti z jiných metod v této třídě. Přístup k sadě private
můžete nastavit tak, aby byl přístup k dispozici místo public
:
public class Person
{
public string? FirstName { get; private set; }
// Omitted for brevity.
}
Nyní lze k FirstName
vlastnosti přistupovat z libovolného kódu, ale lze ji přiřadit pouze z jiného Person
kódu ve třídě.
K sadě nebo získání přístupových objektů můžete přidat libovolný modifikátor omezujícího přístupu. Každý modifikátor přístupu, který umístíte na jednotlivé příslušenství, musí být omezenější než modifikátor přístupu v definici vlastnosti. Výše uvedené je právní, protože vlastnost FirstName
je public
, ale sada příslušenství je private
. Nelze deklarovat private
vlastnost s příslušenstvím public
. Deklarace vlastností lze také deklarovat protected
, internal
, , protected internal
nebo dokonce private
.
Je také legální umístit více omezující modifikátor na get
příslušenství. Můžete public
mít například vlastnost, ale omezit přístup k private
objektu get
. Tento scénář se v praxi provádí jen zřídka.
Jen pro čtení
Můžete také omezit úpravy vlastnosti tak, aby ji bylo možné nastavit pouze v konstruktoru. Třídu můžete upravit Person
následujícím způsobem:
public class Person
{
public Person(string firstName) => FirstName = firstName;
public string FirstName { get; }
// Omitted for brevity.
}
Pouze inicializační
Předchozí příklad vyžaduje, aby volající používali konstruktor, který obsahuje FirstName
parametr. Volající nemůžou k přiřazení hodnoty vlastnosti použít inicializátory objektů. Chcete-li podporovat inicializátory, můžete příslušenství nastavit set
jako přístup init
, jak je znázorněno v následujícím kódu:
public class Person
{
public Person() { }
public Person(string firstName) => FirstName = firstName;
public string? FirstName { get; init; }
// Omitted for brevity.
}
Předchozí příklad umožňuje volajícímu Person
vytvořit pomocí výchozího konstruktoru, i když tento kód nenastaví FirstName
vlastnost. Počínaje jazykem C# 11 můžete vyžadovat , aby volající nastavili tuto vlastnost:
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName) => FirstName = firstName;
public required string FirstName { get; init; }
// Omitted for brevity.
}
Předchozí kód vytvoří do třídy dva doplňky Person
. Nejprve deklarace FirstName
vlastnosti zahrnuje required
modifikátor. To znamená, že jakýkoli kód, který vytvoří novou Person
, musí nastavit tuto vlastnost. Za druhé, konstruktor, který přebírá firstName
parametr má System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute atribut. Tento atribut informuje kompilátor, že tento konstruktor nastaví všechnyrequired
členy.
Důležité
Nezaměňujte required
s nenulovou hodnotou. Je platné nastavit required
vlastnost na null
hodnotu nebo default
. Pokud je typ nenulový, například string
v těchto příkladech, kompilátor vydá upozornění.
Volající musí buď použít konstruktor s SetsRequiredMembers
nebo nastavit FirstName
vlastnost pomocí inicializátoru objektů, jak je znázorněno v následujícím kódu:
var person = new VersionNinePoint2.Person("John");
person = new VersionNinePoint2.Person{ FirstName = "John"};
// Error CS9035: Required member `Person.FirstName` must be set:
//person = new VersionNinePoint2.Person();
Vypočítané vlastnosti
Vlastnost nemusí jednoduše vracet hodnotu pole člena. Můžete vytvořit vlastnosti, které vrátí vypočítanou hodnotu. Pojďme objekt rozšířit Person
tak, aby vrátil celý název vypočítaný zřetězením křestní jména a příjmení:
public class Person
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
public string FullName { get { return $"{FirstName} {LastName}"; } }
}
Výše uvedený příklad používá funkci interpolace řetězců k vytvoření formátovaného řetězce pro celý název.
Můžete také použít člena s výrazem, který poskytuje stručnější způsob, jak vytvořit vypočítanou FullName
vlastnost:
public class Person
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
public string FullName => $"{FirstName} {LastName}";
}
Členové typu výrazy používají syntaxi výrazu lambda k definování metod, které obsahují jeden výraz. Tento výraz vrátí celé jméno objektu osoby.
Vyhodnocené vlastnosti uložené v mezipaměti
Koncept počítané vlastnosti můžete kombinovat s úložištěm a vytvořit vyhodnocenou vlastnost uloženou v mezipaměti. Můžete například aktualizovat FullName
vlastnost tak, aby formátování řetězce proběhlo jenom při prvním přístupu:
public class Person
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
Výše uvedený kód ale obsahuje chybu. Pokud kód aktualizuje hodnotu vlastnosti FirstName
nebo hodnoty LastName
, je dříve vyhodnocené fullName
pole neplatné. set
Upravíte přístupové objekty FirstName
a LastName
vlastnost tak, aby fullName
se pole vypočítalo znovu:
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;
}
}
}
Tato konečná verze vyhodnocuje FullName
vlastnost pouze v případě potřeby. Pokud je dříve počítaná verze platná, použije se. Pokud změna jiného stavu zneplatní dříve počítanou verzi, přepočítá se. Vývojáři, kteří používají tuto třídu, nemusí znát podrobnosti implementace. Žádný z těchto vnitřních změn nemá vliv na použití objektu Person. To je klíčový důvod použití vlastností k zveřejnění datových členů objektu.
Připojení atributů k automaticky implementovaným vlastnostem
Atributy pole lze připojit k kompilátoru vygenerovanému záložnímu poli v automaticky implementovaných vlastnostech. Představte si například revizi Person
třídy, která přidává jedinečnou celočíselnou Id
vlastnost. Vlastnost napíšete Id
pomocí automaticky implementované vlastnosti, ale váš návrh nevolá zachování Id
vlastnosti. Lze NonSerializedAttribute je připojit pouze k polím, nikoli vlastnostem. Pomocí specifikátoru atributu NonSerializedAttribute můžete připojit k záložnímu poli vlastnosti Id
field:
, jak je znázorněno v následujícím příkladu:
public class Person
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
[field:NonSerialized]
public int Id { get; set; }
public string FullName => $"{FirstName} {LastName}";
}
Tato technika funguje pro všechny atributy, které připojíte k backing pole u automaticky implementované vlastnosti.
Implementace INotifyPropertyChanged
Posledním scénářem, kdy potřebujete napsat kód v přístupovém objektu vlastnosti, je podporovat INotifyPropertyChanged rozhraní používané k upozorňovat klienty datové vazby, že se hodnota změnila. Když se změní hodnota vlastnosti, objekt vyvolá INotifyPropertyChanged.PropertyChanged událost označující změnu. Knihovny datových vazeb zase aktualizují prvky zobrazení na základě této změny. Následující kód ukazuje, jak byste implementovali INotifyPropertyChanged
vlastnost FirstName
této třídy osob.
public class Person : INotifyPropertyChanged
{
public string? FirstName
{
get => _firstName;
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("First name must not be blank");
if (value != _firstName)
{
_firstName = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(FirstName)));
}
}
}
private string? _firstName;
public event PropertyChangedEventHandler? PropertyChanged;
}
Operátor ?.
se nazývá podmíněný operátor null. Před vyhodnocením pravé strany operátoru zkontroluje nulový odkaz. Konečným výsledkem je, že pokud k události nejsou žádní odběratelé PropertyChanged
, kód pro vyvolání události se nespustí. V takovém případě by to vyhodilo NullReferenceException
bez této kontroly. Další informace najdete na webu events
. Tento příklad také používá nový nameof
operátor k převodu ze symbolu názvu vlastnosti na jeho textové vyjádření. Použití nameof
může snížit chyby, kdy jste nesprávně zadali název vlastnosti.
Implementace je příkladem případu, INotifyPropertyChanged ve kterém můžete napsat kód ve svých přístupových objektech pro podporu potřebných scénářů.
Sečtením
Vlastnosti jsou formou inteligentních polí ve třídě nebo objektu. Zvnějšku objektu se zobrazí jako pole v objektu. Vlastnosti je však možné implementovat pomocí celé palety funkcí jazyka C#. Můžete zadat ověřování, různé přístupnosti, opožděné vyhodnocení nebo jakékoli požadavky, které vaše scénáře potřebují.
Váš názor
https://aka.ms/ContentUserFeedback.
Připravujeme: V průběhu roku 2024 budeme postupně vyřazovat Problémy GitHubu jako mechanismus zpětné vazby pro obsah a nahradíme ho novým systémem zpětné vazby. Další informace najdete tady:Odeslat a zobrazit názory pro