Свойства

Несколько месяцев назад при написании программы на C++ у автора возникла ситуация, при которой одна из переменных класса (Filename) могла быть производной от другой переменной (Name). Было принято решение воспользоваться идиомой (или архитектурным шаблоном) свойства и написана для производной переменной функция getFileName(). Это привело к необходимости просмотра всего программного кода и замены всех ссылок на эту переменную вызовами getFilename(). Проект был довольно большим, поэтому на это потребовалось немало времени.

Кроме того, пришлось постоянно помнить, что для получения имени файла следует вызывать функцию getFilename() вместо того, чтобы просто обратиться к переменной класса. Это привело к усложнению логической модели программы.

В C# свойства реализованы на уровне синтаксиса языка. Для пользователя свойства выглядят как переменные класса, однако чтение и изменение их значений выполняется функциями класса. Таким образом, пользовательская модель (переменная класса) отделяется от модели реализации (функция класса); пользователь изолируется от конкретных технических решений, что обеспечивает большую гибкость при выборе архитектуры и сопровождении класса.

В .NET Runtime свойства реализуются при помощи небольшого объема дополнительных метаданных, связывающих функции класса с именами свойств. Благодаря этому в одних языках свойства выглядят как свойства, а в других — как обычные функции класса.

Свойства широко используются в библиотеке .NET Base Class Library; более того, открытые поля в ней практически не встречаются.

Функции доступа

При объявлении свойства вы указываете один или два блока программного кода, называемых функциями доступа (accessors). Функции доступа читают и/или задают новое значение свойства. Рассмотрим простой пример.

class Test

{

private string name;

public string Name

{

get

{

return name;

}

set

{

name = value;

}

}

}

В этом классе объявлено свойство Name, для которого определена как функция чтения (getter), так и функция записи (setter). Функция чтения просто возвращает значение закрытой переменной класса, а функция записи обновляет переменную при помощи специального параметра value. При каждом вызове функции записи переменная value содержит присваиваемую величину. Тип value совпадает с типом свойства.

Свойства могут иметь функцию чтения, функцию присваивания или обе функции сразу. Свойство, у которого определена только функция чтения, называется доступным только для чтения. Если у свойства определена только функция записи, оно называется доступным только для записи.

Свойства и наследование

Свойства, как и функции класса, могут объявляться с модификаторами virtual, override и abstract. Модификаторы включаются в объявление свойства и распространяются на обе функции доступа.

При объявлении в производном классе свойства с тем же именем, что и в базовом классе, свойство базового класса скрывается полностью; невозможно скрыть только базовую функцию чтения или записи.

Применение свойств

Свойства отделяют интерфейс класса от его реализации. Они часто применяются в ситуациях, когда свойство является производным от других переменных класса, а также при ускоренной инициализации и выборке настоящего значения лишь тогда, когда оно понадобится пользователю.

Предположим, производитель автомашин хочет получить отчет с текущей информацией о состоянии производства.

using System;

class Auto

{

public Auto(int id, string name)

{

this.id = id;

this.name = name;

}

// Запрос для определения числа произведенных машин

public int ProductionCount

{

get

{

if (productionCount == -1)

{

// Получить количество машин из базы данных.

}

return(productionCount);

}

}

public int SalesCount

{

get

{

if (salesCount == -1)

{

// Обратиться за данными к торговым представителям

}

return(salesCount);

}

}

string name;

int id;

int productionCount = -1;

int salesCount = -1;

}

Свойства ProductionCount и SalesCount инициализируются значением –1. Длительные операции по получению данных откладываются до момента, когда в этих данных действительно возникнет необходимость.

Побочные эффекты при модификации свойств

Свойства также часто применяются в ситуациях, когда функция записи должна выполнить дополнительные операции, не ограничиваясь простым присваиванием. Например, при изменении в корзине количества заказанных единиц товара можно обновить общую стоимость заказа:

using System;

using System.Collections;

class Basket

{

internal void UpdateTotal()

{

total = 0;

foreach (BasketItem item in items)

{

total += item.Total;

}

}

ArrayList items = new ArrayList();

Decimal total;

}

class BasketItem

{

BasketItem(Basket basket)

{

this.basket = basket;

}

public int Quantity

{

get

{

return(quantity);

}

set

{

quantity = value;

basket.UpdateTotal();

}

}

public Decimal Price

{

get

{

return(price);

}

set

{

price = value;

basket.UpdateTotal();

}

}

public Decimal Total

{

get

{

// При покупке 10 и более единиц — скидка 10 %

if (quantity >= 10)

return(quantity * price * 0.90m);

else

return(quantity * price);

}

}

int quantity; // Количество единиц товара

Decimal price; // Цена за единицу

Basket basket; // Обратная ссылка на корзину

}

В этом примере класс Basket содержит массив объектов BasketItem. При изменении цены или количества единиц товара класс Basket оповещается об этом, перебирает все содержимое корзины и обновляет общую стоимость заказа.

Предлагаю ознакомиться с аналогичными статьями: