До сих пор мы использовали в классах только поля и методы по их обработки. В языке C# имеются и другие члены классов, одно из них — это свойства.
Свойство — это особый вид методов, обеспечивающих доступ к полям класса. Для каждого свойства может быть два таких метода: один — для чтения значения поля (get), другой — для записи в него какого-то значения (set). В свойстве могут быть определены оба метода или только какой-либо один. Как правило, одно свойство используют для доступа только к одному полю. Ни для каких других задач свойства не рекомендуется применять.
Формальная запись объявления свойства имеет следующий вид:
Доступ Тип Имя_свойства
{
get
{
return Результат
}
set
{
Поле_класса = value;
}
}
Использование свойства в программе:
Имя_объекта_класса.Имя_свойства = Выражение;
или
Переменная = Имя_объекта_класса.Имя_свойства;
Здесь
Доступ — обычно это слово public (общий доступ), но допустимо использовать protected, private, internal, а также static и new;
Тип — тип свойства. Должен совпадать с типом поля, с которым связано свойство;
Имя_свойства — задаётся по общим правилам для идентификаторов;
get — ключевое слово, оно определяет метод для получения (чтения) значения;
set — ключевое слово, оно определяет метод для изменения (записи) значения;
value — ключевое слово, используется в методе set как неявный параметр и содержит значение, которое необходимо присвоить полю;
Результат — обычно это значение поля класса (у нас далее обозначается как Поле_класса), связанного со свойством;
Переменная и Выражение — должны иметь тот же тип, что и тип свойства.
Для большей ясности приведём пример простой программы, использующей класс со свойством.
Пример. Для класса, моделирующего работу с окружностью, создадим свойство для изменения радиуса окружности.
Возможный текст программы:
using System;
namespace Prim_Svojstvo
{
public class Okr
{
int x, y, r; // координаты центра окружности и её радиус
public Okr()
{
x = y = 100;
r = 10;
}
public Okr(int x0, int y0, int r0)
{
x = x0;
y = y0;
r = r0;
}
public void print()
{
Console.WriteLine("x = {0} y = {1} r = {2}", x, y, r);
}
public int Radius
{
set
{
if(value > 0)
r = value;
else
{
Console.WriteLine("Недопустимое значение для поля: {0}", value);
Console.WriteLine("Поле сохраняет прежнее значение: {0}", r);
}
}
get
{
return r;
}
}
}
class Program
{
public static void Main(string[] args)
{
Okr x = new Okr(25, 45, 5);
x.print();
x.Radius = 4;
x.print();
int r2 = x.Radius * 2;
Console.WriteLine("Удвоенное значение радиуса r2 = {0}", r2);
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}
Результат выполнения программы:
x = 25 y = 45 r = 5
x = 25 y = 45 r = 4
Удвоенное значение радиуса r2 = 8
Press any key to continue . . .
Пояснение к программе. В данной программе использовано свойство Radius, которое обеспечивает полный доступ к закрытому полю r. При необходимости можно было бы определить для данного свойства только метод set (обеспечиваем доступ по записи) или только метод get (доступ только по чтению). Применяется свойство в методе Main() точно также, как если бы это было поле.
Каков смысл в использовании свойств? Какие преимущества получает программист, применяющий в тексте своей программы свойства?
Свойства применяют для доступа к полям, которые, как правило, всегда делают закрытыми (private) или защищёнными (protected).
Возникает вопрос: а не проще ли тогда сделать поле открытым (public), да и обращаться к полю везде, где это потребуется? Да, проще, но в этом случае заметно снижается безопасность данных. Именно для того, чтобы случайно не изменить поле, его и делают закрытым (меньше возможностей по изменению поля — менее вероятна неверная, случайная модификация данных). К тому же, если требуется доступ к полю только по чтению, зачем его делать открытым (доступным и по чтению, и по записи)?
Проблему можно решить, применяя отдельные методы для чтения поля и для его записи. Например, для рассмотренной выше задачи можно было бы для получения доступа по чтению к полю r написать метод типа
int getRadius()
{
return r;
}
Понадобился бы метод для доступа по записи — можно добавить ещё метод
void setRadius(int r0)
{
if(r0 > 0)
r = r0;
else
{
Console.WriteLine("Недопустимое значение для поля: {0}", r0);
Console.WriteLine("Поле сохраняет прежнее значение: {0}", r);
}
}
Хорош такой подход к решению проблемы? Вполне. Безопасность поля обеспечена, обеспечена гибкость при доступе к данным: можно сделать доступ по чтению-записи, используя оба метода, или только по чтению (в классе есть только метод getRadius()), или только по записи (в классе имеется только метод setRadius(int r0)).
А в чём недостаток этого подхода? Во-первых, увеличивается количество имён (для работы с одним полем надо пользоваться двумя разными методами), а чем больше разных имён в программе, тем сложнее с ней работать. Во-вторых, для обращения к полям используется функциональный способ (явный вызов методов), что менее удобно, чем обращение к переменным или к полям, где применяется операторная форма записи.
Поведём итог. Свойства в полной мере используют достоинства функционального подхода: позволяют гибко настраивать доступ к полю, обеспечивать все необходимые проверки, и в тоже время имеют ряд преимуществ по сравнению с функциональным подходом: имя (имя свойства) только одно, и форма записи — операторная, т. е. более естественная. Согласитесь, что, оператор, в котором использовано свойство
r2 = x.Radius * 2;
выглядит более естественно, чем оператор с вызовом метода
r2 = x.getRadius() * 2;
Возникает вопрос: есть ли недостатки у свойств? На примере нашего свойства Radius можно сказать следующее: относительно отдельных методов типа getRadius() и setRadius(), свойство Radius, естественно, недостатков не имеет, а вот относительно непосредственного обращению к полю r — да, имеет. Главное — доступ к полю напрямую осуществляется гораздо быстрее, чем с использованием свойства. Но достоинства свойств с лихвой перевешивают этот недостаток.
|
|
|