Использование свойств (Руководство по программированию в C#)Using Properties (C# Programming Guide)

Свойства сочетают в себе возможности полей и методов.Properties combine aspects of both fields and methods. Пользователю объекта свойство представляется как поле, и для доступа к нему применяется тот же синтаксис.To the user of an object, a property appears to be a field, accessing the property requires the same syntax. При реализации класса свойство представляется в виде одного или двух блоков кода для методов доступа get и (или) set.To the implementer of a class, a property is one or two code blocks, representing a get accessor and/or a set accessor. Блок кода для метода доступа get выполняется только при считывании свойства, а для метода set — при присвоении свойству нового значения.The code block for the get accessor is executed when the property is read; the code block for the set accessor is executed when the property is assigned a new value. Свойство без метода доступа set доступно только для чтения.A property without a set accessor is considered read-only. Свойство без метода доступа get доступно только для записи.A property without a get accessor is considered write-only. Свойство, для которого определены оба этих метода, доступно для чтения и записи.A property that has both accessors is read-write.

В отличие от полей, свойства не классифицируются как переменные.Unlike fields, properties are not classified as variables. Соответственно, нельзя передать свойство в качестве параметра ref или out.Therefore, you cannot pass a property as a ref or out parameter.

Свойства нашли широкое применение в программировании. Их можно использовать для проверки данных перед подтверждением изменения; для прозрачного предоставления доступа к данным класса, которые фактически извлекаются из других источников, например из базы данных; для выполнения действия при изменении данных (например, в этом случае может создаваться событие или изменяться значение других полей).Properties have many uses: they can validate data before allowing a change; they can transparently expose data on a class where that data is actually retrieved from some other source, such as a database; they can take an action when data is changed, such as raising an event, or changing the value of other fields.

При объявлении свойств в блоке класса указывается уровень доступа поля, затем тип и имя свойства, а после этого блок кода, в котором объявляются методы доступа get и (или) set.Properties are declared in the class block by specifying the access level of the field, followed by the type of the property, followed by the name of the property, and followed by a code block that declares a get-accessor and/or a set accessor. Например:For example:

public class Date
{
    private int _month = 7;  // Backing store

    public int Month
    {
        get => _month;
        set
        {
            if ((value > 0) && (value < 13))
            {
                _month = value;
            }
        }
    }
}

В этом примере Month объявляется как свойство, а метод доступа set обеспечивает установку значения Month в диапазоне от 1 до 12.In this example, Month is declared as a property so that the set accessor can make sure that the Month value is set between 1 and 12. Для отслеживания фактического значения свойство Month использует частное поле.The Month property uses a private field to track the actual value. Фактическое местоположение данных свойства часто называется "резервным хранилищем".The real location of a property's data is often referred to as the property's "backing store." Как правило, в качестве резервного хранилища свойства используют частные поля.It is common for properties to use private fields as a backing store. Поле помечается как частное для того, чтобы гарантировать возможность его изменения только посредством вызова свойства.The field is marked private in order to make sure that it can only be changed by calling the property. Дополнительные сведения об ограничениях открытого и закрытого доступа см. в разделе Модификаторы доступа.For more information about public and private access restrictions, see Access Modifiers.

Автоматически реализуемые свойства поддерживают упрощенный синтаксис для простых объявлений свойств.Auto-implemented properties provide simplified syntax for simple property declarations. Дополнительные сведения см. в разделе Автоматически реализуемые свойства.For more information, see Auto-Implemented Properties.

Метод доступа getThe get Accessor

Тело метода доступа get похоже на тело метода.The body of the get accessor resembles that of a method. Оно должно возвращать значение заданного типа свойства.It must return a value of the property type. Выполнение метода доступа get эквивалентно считыванию значения поля.The execution of the get accessor is equivalent to reading the value of the field. Например, если включена оптимизация и метод доступа get возвращает частную переменную, вызов метода доступа get определяется компилятором как встроенный, что позволяет исключить затраты ресурсов на вызов метода.For example, when you are returning the private variable from the get accessor and optimizations are enabled, the call to the get accessor method is inlined by the compiler so there is no method-call overhead. Тем не менее виртуальный метод доступа get не может определяться как встроенный, поскольку во время компиляции компилятору не известно, как метод может быть фактически вызван во время выполнения.However, a virtual get accessor method cannot be inlined because the compiler does not know at compile-time which method may actually be called at run time. Ниже показан метод доступа get, возвращающий значение частного поля _name:The following is a get accessor that returns the value of a private field _name:

class Person
{
    private string _name;  // the name field
    public string Name => _name;     // the Name property            
}

При ссылке на свойство (кроме случаев, когда свойство является целью присваивания) вызывается метод доступа get, который считывает значение свойства.When you reference the property, except as the target of an assignment, the get accessor is invoked to read the value of the property. Например:For example:

Person person = new Person();
//...

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

Метод доступа get должен завершаться инструкцией return или throw, при этом управление не может передаваться из тела метода доступа.The get accessor must end in a return or throw statement, and control cannot flow off the accessor body.

Изменение состояния объекта с помощью метода доступа get считается ошибочным стилем программирования.It is a bad programming style to change the state of the object by using the get accessor. Например, побочным эффектом следующего метода доступа является изменение состояния объекта каждый раз при доступе к полю _number.For example, the following accessor produces the side effect of changing the state of the object every time that the _number field is accessed.

private int _number;
public int Number => _number++;	// Don't do this        

Метод доступа get можно использовать для возврата значения поля напрямую или после вычисления.The get accessor can be used to return the field value or to compute it and return it. Например:For example:

class Employee
{
    private string _name;
    public string Name => _name != null ? _name : "NA"; 
}

Если в предыдущем фрагменте кода свойству Name не присвоено значение, будет возвращено значение NA.In the previous code segment, if you do not assign a value to the Name property, it will return the value NA.

Метод доступа setThe set Accessor

Метод доступа set похож на метод с типом возвращаемого значения void.The set accessor resembles a method whose return type is void. В нем используется неявный параметр value, тип которого соответствует типу свойства.It uses an implicit parameter called value, whose type is the type of the property. В следующем примере метод доступа set добавляется к свойству Name:In the following example, a set accessor is added to the Name property:

class Person
{
    private string _name;  // the name field
    public string Name    // the Name property
    {
        get => _name;
        set => _name = value;
    }
}

При присвоении значения свойству вызывается метод доступа set с аргументом, содержащим новое значение.When you assign a value to the property, the set accessor is invoked by using an argument that provides the new value. Например:For example:

Person person = new Person();
person.Name = "Joe";  // the set accessor is invoked here                

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

Использование имени явного параметра (value) для объявления локальной переменной в методе доступа set является ошибкой.It is an error to use the implicit parameter name, value, for a local variable declaration in a set accessor.

ПримечанияRemarks

Свойства могут быть помечены как public, private, protected, internal, protected internal или private protected.Properties can be marked as public, private, protected, internal, protected internal or private protected. Эти модификаторы доступа определяют, каким образом пользователи класса смогут получать доступ к свойству.These access modifiers define how users of the class can access the property. Методы доступа get и set для одного свойства могут иметь разные модификаторы доступа.The get and set accessors for the same property may have different access modifiers. Например, метод доступа get может иметь модификатор public, разрешающий доступ из-за пределов типа только для чтения, а метод доступа set — модификатор private или protected.For example, the get may be public to allow read-only access from outside the type, and the set may be private or protected. Дополнительные сведения см. в статье Модификаторы доступа.For more information, see Access Modifiers.

Свойство может быть объявлено как статическое с помощью ключевого слова static.A property may be declared as a static property by using the static keyword. Это делает свойство доступным для вызывающих объектов в любое время, даже если экземпляр класса не существует.This makes the property available to callers at any time, even if no instance of the class exists. Дополнительные сведения см. в статье Статические классы и члены статических классов.For more information, see Static Classes and Static Class Members.

Свойство может быть помечено как виртуальное с помощью ключевого слова virtual.A property may be marked as a virtual property by using the virtual keyword. Это позволяет производным классам переопределять поведение свойства с помощью ключевого слова override.This enables derived classes to override the property behavior by using the override keyword. Дополнительные сведения об этих параметрах см. в разделе Наследование.For more information about these options, see Inheritance.

Свойство, переопределяющее виртуальное свойство, также может быть запечатанным (sealed). Это указывает, что для производных классов оно больше не является виртуальным.A property overriding a virtual property can also be sealed, specifying that for derived classes it is no longer virtual. Наконец, свойство можно объявить абстрактным (abstract).Lastly, a property can be declared abstract. Это означает, что в классе не будет определена реализация такого свойства, и в производных классах должны использоваться собственные реализации.This means that there is no implementation in the class, and derived classes must write their own implementation. Дополнительные сведения об этих параметрах см. в разделе Абстрактные и запечатанные классы и члены классов.For more information about these options, see Abstract and Sealed Classes and Class Members.

Примечание

Использование модификаторов virtual, abstract или override в методе доступа статического (static) свойства является ошибкой.It is an error to use a virtual, abstract, or override modifier on an accessor of a static property.

ПримерExample

В этом примере демонстрируются свойства экземпляра, а также статические и доступные только для чтения свойства.This example demonstrates instance, static, and read-only properties. Этот метод принимает введенное с клавиатуры имя сотрудника, увеличивает значение NumberOfEmployees на 1, после чего отображает имя и номер сотрудника.It accepts the name of the employee from the keyboard, increments NumberOfEmployees by 1, and displays the Employee name and number.

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:            
}

class TestEmployee
{
    static void Main()
    {
        Employee.NumberOfEmployees = 107;
        Employee e1 = new Employee();
        e1.Name = "Claude Vige";

        System.Console.WriteLine("Employee number: {0}", Employee.Counter);
        System.Console.WriteLine("Employee name: {0}", e1.Name);
    }
}
/* Output:
    Employee number: 108
    Employee name: Claude Vige
*/

ПримерExample

В этом примере демонстрируется доступ к свойству базового класса, которое скрыто в производном классе другим свойством с таким же именем:This example demonstrates how to access a property in a base class that is hidden by another property that has the same name in a derived class:

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
{
    static void Main()
    {
        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
*/

На что следует обратить внимание в предыдущем примере:The following are important points in the previous example:

  • Свойство Name в производном классе скрывает свойство Name базового класса.The property Name in the derived class hides the property Name in the base class. В таком случае в объявлении свойства в производном классе используется модификатор new:In such a case, the new modifier is used in the declaration of the property in the derived class:

    public new string Name
    
  • Для доступа к скрытому свойству в базовом классе используется приведение (Employee):The cast (Employee) is used to access the hidden property in the base class:

    ((Employee)m1).Name = "Mary";
    

    Дополнительные сведения о скрытии элементов см. в разделе Модификатор new.For more information about hiding members, see the new Modifier.

ПримерExample

В этом примере два класса (Cube и Square) реализуют абстрактный класс Shape и переопределяют его абстрактное свойство Area.In this example, two classes, Cube and Square, implement an abstract class, Shape, and override its abstract Area property. Обратите внимание на использование модификатора override в свойствах.Note the use of the override modifier on the properties. Программа принимает введенную длину стороны, на основании которой рассчитывает площади квадрата и куба.The program accepts the side as an input and calculates the areas for the square and cube. Также принимается введенное значение площади, на основании которой рассчитываются длины сторон квадрата и куба.It also accepts the area as an input and calculates the corresponding side for the square and cube.

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
*/

См. такжеSee also