Použití vlastností (Průvodce programováním v C#)
Vlastnosti kombinují aspekty polí i metod. Uživateli objektu se zdá, že vlastnost je pole; přístup k vlastnosti vyžaduje stejnou syntaxi. Pro implementátora třídy je vlastnost jeden nebo dva bloky kódu, které představují přístupovou metodu get
a/nebo set
či init
přístupovou metodu. Blok kódu pro přístup se get
spustí, když se vlastnost čte; blok kódu pro set
nebo init
přístup se spustí, když je vlastnosti přiřazena hodnota. Vlastnost bez přístupového objektu set
je považována za jen pro čtení. Vlastnost bez přístupového prvku get
se považuje za pouze zapisovatelnou. Vlastnost, která má oba přístupové metody, je čtení a zápis. Můžete použít přístupový objekt init
místo přístupového objektu set
, abyste umožnili nastavení vlastnosti jako součást inicializace objektu, ale jinak ji ponechali jen pro čtení.
Na rozdíl od polí nejsou vlastnosti klasifikovány jako proměnné. Proto nemůžete předat vlastnost jako ref
nebo out
parametr.
Vlastnosti mají mnoho použití:
- Před povolením změny můžou ověřit data.
- Mohou transparentně zveřejnit data ve třídě, kde se tato data načítají z jiného zdroje, například z databáze.
- Můžou provést akci při změně dat, například při vyvolání události nebo změně hodnoty jiných polí.
Vlastnosti jsou deklarovány v bloku třídy zadáním úrovně přístupu vlastnosti, následované typem vlastnosti, následovaným názvem vlastnosti a následným blokem kódu, který deklaruje get
-accessor a/nebo set
-accessor. Příklad:
public class Date
{
private int _month = 7; // Backing store
public int Month
{
get => _month;
set
{
if ((value > 0) && (value < 13))
{
_month = value;
}
}
}
}
V tomto příkladu je Month
deklarován jako vlastnost, aby přístupový prvek set
mohl zajistit, že hodnota Month
bude nastavena mezi 1 a 12. Vlastnost Month
používá soukromé pole ke sledování skutečné hodnoty. Skutečné umístění dat vlastnosti se často označuje jako "záložní úložiště". Vlastnosti běžně používají privátní pole jako záložní úložiště. Pole je označené jako soukromé, aby se zajistilo, že je možné ho změnit pouze voláním vlastnosti. Další informace o omezeních veřejného a privátního přístupu najdete v tématu Modifikátory přístupu. Automaticky implementované vlastnosti poskytují zjednodušenou syntaxi pro jednoduché deklarace vlastností. Další informace naleznete v tématu Automaticky implementované vlastnosti.
Počínaje jazykem C# 13 můžete pomocí vlastností založených na polích přidat ověřování k set
přístupové objektu automaticky implementované vlastnosti, jak je znázorněno v následujícím příkladu:
public class DateExample
{
public int Month
{
get;
set
{
if ((value > 0) && (value < 13))
{
field = value;
}
}
}
}
Důležité
Klíčové slovo field
je předváděcí funkce ve verzi C# 13. Musíte používat .NET 9 a nastavit prvek <LangVersion>
na preview
ve svém souboru projektu, abyste mohli použít kontextové klíčové slovo field
.
Měli byste být opatrní při použití funkce klíčového slova field
ve třídě, která má pole s názvem field
. Nové field
klíčové slovo stínuje pole pojmenované field
v oboru přístupového objektu vlastnosti. Můžete změnit název field
proměnné nebo pomocí @
tokenu odkazovat na field
identifikátor jako @field
. Další informace najdete ve specifikaci funkce pro field
klíčové slovo.
Přístupový objekt get
Tělo get
příslušenství se podobá tělu metody. Musí vrátit hodnotu typu vlastnosti. Kompilátor jazyka C# a kompilátor JIT (Just-in-Time) detekují běžné vzory pro implementaci přístupového objektu get
a optimalizuje tyto vzory. Například přístupový get
objekt, který vrací pole bez provedení výpočtu, je pravděpodobně optimalizován na čtení paměti daného pole. Automaticky implementované vlastnosti se řídí tímto vzorem a využívají tyto optimalizace. Nelze však vložit virtuální get
přístupovou metodu, protože kompilátor neví během kompilace, která metoda může být skutečně zavolána za běhu. Následující příklad ukazuje přístupový objekt get
, který vrací hodnotu soukromého pole _name
:
class Employee
{
private string _name; // the name field
public string Name => _name; // the Name property
}
Při odkazování na vlastnost, s výjimkou, kdy je cílem přiřazení, je vyvolán přistupovač get
ke čtení hodnoty vlastnosti. Příklad:
var employee = new Employee();
//...
System.Console.Write(employee.Name); // the get accessor is invoked here
Přístupový get
prvek musí být členem s tělem tvořeným výrazem nebo končit příkazem return nebo throw a řízení nemůže opustit tělo přístupového prvku.
Varování
Obecně se jedná o špatný programovací styl, jak změnit stav objektu pomocí přístupového objektu get
. Jednou z výjimek tohoto pravidla je opožděná vyhodnocená vlastnost, kde se hodnota vlastnosti vypočítá pouze při prvním přístupu.
Tento get
přístupový prvek lze použít k vrácení hodnoty pole nebo k jejímu výpočtu a následnému vrácení. Příklad:
class Manager
{
private string _name;
public string Name => _name != null ? _name : "NA";
}
Pokud v předchozím příkladu nepřiřadíte hodnotu vlastnosti Name
, vrátí hodnotu NA
.
Metoda nastavování
Přístupový set
objekt se podobá metodě, jejíž návratový typ je void. Používá implicitní parametr s názvem value
, jehož typ je typ vlastnosti. Kompilátor a kompilátor JIT také rozpoznávají běžné vzory pro určitý set
objekt nebo init
příslušenství. Tyto běžné vzory jsou optimalizované, přímo zapisují paměť pro podpůrné pole. V následujícím příkladu je do vlastnosti set
přidán přístupový prvek Name
.
class Student
{
private string _name; // the name field
public string Name // the Name property
{
get => _name;
set => _name = value;
}
}
Přiřadíte-li hodnotu k vlastnosti, je pomocí argumentu, který poskytuje novou hodnotu, vyvolán přístupový objekt set
. Příklad:
var student = new Student();
student.Name = "Joe"; // the set accessor is invoked here
System.Console.Write(student.Name); // the get accessor is invoked here
Jedná se o chybu použití implicitního názvu parametru , value
pro deklaraci místní proměnné v přístupovém objektu set
.
Inicializační příslušenství
Kód pro vytvoření přístupového objektu init
je stejný jako kód pro vytvoření přístupového objektu set
, pouze použijete klíčové slovo init
místo set
. Rozdíl je v tom, že
Poznámky
Vlastnosti mohou být označeny jako public
, private
, protected
internal
, , protected internal
, nebo private protected
. Tyto modifikátory přístupu definují, jak uživatelé třídy mají přístup k vlastnosti. Pro přístupové objekty get
a set
pro stejnou vlastnost mohou být použity různé modifikátory přístupu. Například get
může být nastaven tak, aby umožňoval přístup jen pro čtení z vnějšku typu, a public
může být set
nebo private
. Další informace naleznete v tématu Přístupové modifikátory.
Vlastnost lze deklarovat jako statickou vlastnost pomocí klíčového static
slova. Statické vlastnosti jsou kdykoli k dispozici volajícím, i když žádná instance třídy neexistuje. Další informace naleznete v tématu Statické třídy a statičtí členové tříd.
Vlastnost lze označit jako virtuální vlastnost pomocí virtuálního klíčového slova. Virtuální vlastnosti umožňují odvozeným třídám přepsat chování vlastnosti pomocí klíčového slova override. Další informace o těchto možnostech naleznete v tématu Dědičnost.
Vlastnost přepisující virtuální vlastnost může být také zapečetěna, což specifikuje, že pro odvozené třídy již není virtuální. Nakonec lze deklarovat abstraktní vlastnost. Abstraktní vlastnosti nedefinují implementaci ve třídě a odvozené třídy musí zapsat vlastní implementaci. Další informace o těchto možnostech naleznete v tématu Abstraktní a zapečetěné třídy a členy třídy.
Poznámka:
Jedná se o chybu použití modifikátoru virtuálního, abstraktního nebo override u přístupové metody statické vlastnosti.
Příklady
Tento příklad ukazuje vlastnosti instance, statické a jen pro čtení. Přijme jméno zaměstnance z klávesnice, zvýší NumberOfEmployees
o 1 a zobrazí jméno a číslo zaměstnance.
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:
}
Příklad skryté vlastnosti
Tento příklad ukazuje, jak získat přístup k vlastnosti v základní třídě, která je skrytá jinou vlastností, která má stejný název v odvozené třídě:
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
*/
V předchozím příkladu jsou důležité následující body:
- Vlastnost
Name
v odvozené třídě skryje vlastnostName
v základní třídě. V takovém případěnew
se modifikátor používá v deklaraci vlastnosti v odvozené třídě:public new string Name
- Obsazení
(Employee)
se používá pro přístup ke skryté vlastnosti v základní třídě.((Employee)m1).Name = "Mary";
Další informace o skrytí členů naleznete v novém modifikátoru.
Příklad přepsání vlastnosti
V tomto příkladu dvě třídy Cube
a Square
implementují abstraktní třídu Shape
a přepisují její abstraktní vlastnost Area
. Všimněte si použití modifikátoru override u vlastností. Program přijme stranu jako vstup a vypočítá plochy pro čtverec a krychli. Přijímá také oblast jako vstup a vypočítá odpovídající stranu čtverce a datové krychle.
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
*/