Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции ЛИПО .doc
Скачиваний:
0
Добавлен:
06.12.2018
Размер:
441.86 Кб
Скачать

Свойства

Свойства обеспечивают надежную и элегантную поддержку одного из принципов ООП - принципа инкапсуляции. Они позволяют обращаться к закрытым (private) перемен­ным экземпляра так же, как и к открытым (public), т.е. без громоздких методов называемых аксессорами и мутаторами, рассмотренных ранее. Применение свойств не приводит к нарушению за­шиты и сокрытия данных (что необходимо при использовании переменных экземпляра private), поскольку свойства реализуются внутри класса почти так же, как и методы.

Хотя свойство для внешнего мира представляет собой переменную экземпляра public, оно реализовано способом, похожим на метод (ведь свойство тоже может исполнять опе­раторы). Благодаря таким возможностям свойства удобно применять вместо аксессоров и мутаторов.

В соответствии с принципом инкапсуляции, переменные экземпляра должны быть скрыты от остальной программы. Для этого в языке С# они объявляются со спецификатором доступности private. В примерах, представленных далее, доступ к закрытым переменным осуществляется посредством двух методов: аксессора и мутатора, имена которых по соглашению имеют префикс get и set. Например, GetDistance() и SetDistance(). Эти методы могут также проверять значения, присваиваемые переменным, или выполнять другие операции Пример использования аксессора и мутатора приведен в листинге программы.

Пример. Написать программу «Студент», состоящую из 2-х классов: класса «Студент» и класса «Тест». Класс «Студент» описывает объект студент и имеет 3 переменных экземпляра: Имя, Фамилия, и оценка по ЛИПО. Причем, последняя является закрытой переменной и для нее необходимо написать аксессор с мутатором, причем они должны проверять, чтобы оценка находилась в пределах от 2 до 5 .

using System;

using System.Collections.Generic;

using System.Text;

class student

{

public string first_name = "", second_name = "";

private byte mark;

// Аксессор:

public int get_mark()

{

return mark;

}

// Мутатор:

public void set_mark(byte new_mark)

{

if (new_mark < 1 || new_mark > 5)

{

Console.WriteLine("Ошибка, вводимые данные должны находится в диопазоне от 1 до 5 !!!");

}

else

{

mark = new_mark;

}

}

}

//----------------------------------------------------------------------

class test

{

static void Main(string[] args)

{

student student_1 = new student();

student_1.first_name = "Петров";

student_1.second_name = "Петр";

student_1.set_mark(2);

Console.WriteLine("Студент " + student_1.first_name + " " + student_1.second_name + " имеет оценку по ЛИПО: " + student_1.get_mark());

Console.ReadLine();

}

}

Класс student содержит закрытую переменную экземпляра: mark. В данном примере переменной mark нельзя присвоить значение, меньшее 1 или большее 5. Для решения задачи пишем два метода аксессор get_mark, который возвращает значение закрытой переменной экземпляра mark :

public int get_mark()

{

return mark;

}

и мутатор set_mark, который изменяет значение переменной экземпляра mark:

public void set_mark(byte new_mark)

{

if (new_mark < 1 || new_mark > 5)

{

Console.WriteLine("Ошибка, вводимые данные должны находится в диопазоне от 1 до 5 !!!");

}

else

{

mark = new_mark;

}

}

Метод Main класса Test - внешний по отношению к классу student. Для работы с переменной mark в нем используются описанные выше методы set_mark и get_mark.

class test

{

static void Main(string[] args)

{

student student_1 = new student();

student_1.first_name = "Петров";

student_1.second_name = "Петр";

student_1.set_mark(2);

Console.WriteLine("Студент " + student_1.first_name + " " + student_1.second_name + " имеет оценку по ЛИПО: " + student_1.get_mark());

Console.ReadLine();

}

}

Аксессор и мутатор широко используются программистами в таких языках, как Java и C++, где нет аналога свойствам С#. Их недостаток — несоответствие между синтак­сисом доступа к переменным экземпляра внутри объекта и за его пределами.

При обра­щении к переменной экземпляра внутри объекта применяется не метод, а стандартный синтаксис: mark = new_mark; Совместимость синтаксиса предполагает, что и во внешнем по отношению к объек­ту коде можно использовать не метод-мутатор, а стандартный оператор присвоения: mark = 2;.

Однако, без использования свойств такие возможности могут быть достигнуты только объявлением переменной mark со спецификатором public (при этом будут от­брошены важные операторы, содержащиеся в аксессоре и мутаторе).

Свойство (как структурный элемент языка С#) позволяет:

внешнему пользовате­лю объекта обращаться к переменной экземпляра с синтаксисом (без метода), приве­денным ранее;

переменным экземпляра оставаться закрытыми и в то же время, свой­ство может содержать операторы, которые в ином случае могут находиться только в методах доступа (аксессоре и мутаторе).

Рассмотрим синтаксический блок свойства:

Объявление_свойства::=

[<Спецификатор_доступности>]<Тип><Идентификатор>

{

[<блок_get>]

[<блок_set>]

}

где

<Спецификатор_доступности>:

::= public

::= private

::= protected

::= internal

::= protected internal

<блок_get>:

::= get

{

[<Операторы>]

return <Выражение>

}

<блок_set>:

::= set

{

[<Операторы> с использованием ключевого слова value]

}

Стоит отметить несколько особенностей синтаксиса свойств:

  • тип <Выражение>, расположенного после ключевого слова return в <блоке_get>, должен совпадать с <Тип> в заголовке свойства;

  • параметр value в <блоке_set> представляет значение, присваиваемое свойству, и принадлежит типу <Тип> из заголовка;

  • имя value задано компилятором — его нельзя изменить;

  • свойство может содержать только <Блок_get>, только <Блок_set>, или оба блока.

Свойство не представляет определенную переменную экземпляра (хотя зачастую свойства применяются именно так), а имитирует ее. К примеру, свойство может называться Average и подсчитывать в своем блоке get среднее значение несколь­ких переменных экземпляра.

Клиенту (клиентом называется часть программы, которая вызывает свойства и методы класса или объекта) удобнее использовать свойства, а не аксессоры и мутаторы по сле­дующим причинам:

свойства обеспечивают согласованный синтаксис при обращении к переменной эк­земпляра внутри объекта и за его пределами;

свойства позволяют программисту не заботиться о том, является переменная эк­земпляра public или private, определены ли для нее аксессор и мутатор и каков их формат;

как правило, для одной переменной экземпляра (к которой требуется доступ) ис­пользуется один аксессор и один мутатор (т. е. два метода), а свойство требуется только одно, таким образом, вместо анализа сорока аксессоров и мутаторов пользо­вателю достаточно просмотреть двадцать свойств.

cвойства объединяют аксессор и мутатор в одну структуру, обычно, когда для доступа к переменной экземпляра используются два этих метода, при изменении в одном из них необходима и модификация другого, однако синтаксически эти методы различны и ни­чем не отличаются от любых других методов объекта (синтаксис никак не подчеркивает, что они должны применяться в паре), такая структура, как свойство решает эту пробле­му: обе смысловые части (аксессор — блок get, и мутатор — блок set) собраны в одной точке кода.

Далее рассмотрим программу, которая позволяет при помощи свойств реализовать аналогичную функциональность:

using System;

using System.Collections.Generic;

using System.Text;

namespace Студент_свойства_

{

class student

{

public string first_name = "", second_name = "";

private byte mark;

// Свойство:

public byte Mark

{

get

{

return mark;

}

set

{

if (value < 1 || value > 5)

{

Console.WriteLine("Ошибка, вводимые данные должны находится в диопазоне от 1 до 5 !!!");

}

else

{

mark = value;

}

}

}

}

//----------------------------------------------------------------------

class test

{

static void Main(string[] args)

{

student student_2 = new student();

student_2.first_name = "Иванов";

student_2.second_name = "Иван";

student_2.Mark=5;

Console.WriteLine("Студент " + student_2.first_name + " " + student_2.second_name + " имеет оценку по ЛИПО: " + student_2.Mark);

Console.ReadLine();

}

}

}

Класс student содержит свойств Mark, которое обеспечивает доступ к закрытой переменной экземпляра mark. Рассмотрим свойство Mark. Свойство состоит из заголовка и блока, заключенного в фигурные скобки В заголовке можно определить спецификатор доступности (в данном случае public), тип свойства (byte) и его имя (Mark). Блок свойства состоит из блока get , содержащего те же операторы, что и аксессор, и блока set, содержащего операторы мутатора. Клиентская часть (т. е., попросту, пользователь класса, а в данном случае — метод Main) может теперь вызвать свойство Mark как собственную переменную экземпляра:

student_2.Mark=5;

Специальный параметр value, представляет присваиваемое значение (в данном случае 5). Сре­да исполнения автоматически присваивает параметру value значение 5. Таким образом, значение 5 присваивается переменной экземпляра mark.

Чтение значения mark осуществляется следующим образом:

student_2.Mark

Инициируя исполнение операторов блока get. После исполнения оператора с ключе­вым словом return блок get завершается и возвращает выражение, следую­щее за словом return. Значение return в таком контексте идентично его значению в обыч­ном методе.

Если требуется только установить значение переменной экземпляра (когда она пред­назначена только для записи) или только прочесть его (переменная только для чтения), можно определить только один блок: set или get. Важно, что хотя бы один из них дол­жен присутствовать в свойстве.