Тверской государственный университет
Опубликован: 13.09.2006 | Доступ: свободный | Студентов: 3491 / 369 | Оценка: 4.65 / 4.29 | Длительность: 30:37:00
Специальности: Программист, Менеджер
Лекция 5:

Классы и объекты

< Лекция 4 || Лекция 5: 123456 || Лекция 6 >

Синтаксис Let, Get и Set

Оператор Property Let используется для установки значения терминального свойства и имеет следующий синтаксис:

[Public | Private | Friend] [Static] Property Let имя-свойства
	([список-параметров,] значение)
	[Операторы]
	[Exit Property]
	[Операторы]
End Property

Оператор Property Set используется для установки значения свойства - участника (объекта) и имеет следующий синтаксис:

[Public | Private | Friend] [Static] Property Set имя-свойства
	([список-параметров,] ссылка)
	[Операторы]
	[Exit Property]
	[Операторы]
End Property

Оператор Property Get используется для получения значения свойства и имеет следующий синтаксис:

[Public | Private | Friend] [Static] Property Get имя-свойства
	[(список-параметров)] [As Type]
	[Операторы]
	[Exit Property]
	[Операторы]
	[имя-свойства = выражение] 
End Property

Рассмотрим детали синтаксиса:

  • Ключевое слово Public означает доступность Property - свойств во всех процедурах во всех модулях во всех проектах, если только нет дополнительных ограничений. Ключевое слово Private хотя и возможно синтаксически, но лишено смысла, - Property пишут для того, чтобы они были открытыми.
  • Ключевое слово Static, как обычно, означает, что значения локальных переменных процедуры, если они есть, не будут изменяться в промежутке между ее вызовами.
  • Аргумент имя-свойства задает имя определяемого и изменяемого свойства. Заметьте, что когда задается пара процедур Property Let (Set) - Get или все три процедуры, все они имеют одно и тоже имя.
  • Необязательный список-параметров используется чаще всего при задании свойства, значения которого образуют массив. В нем через запятую перечисляются параметры, передаваемые процедуре, например, в качестве индексов. Парные процедуры должны иметь один и тот же список параметров. Синтаксис списка-параметров такой же, как и у параметров обычных процедур, определяемых оператором Sub. Как обычно, если нужно передать переменное число параметров, то можно использовать ParamArray.
  • Параметр значение в Property Let и ссылка в Property Set - это имя переменной (объекта), значение которого передается свойству. Тип значения, возвращаемого процедурой Property Get, должен совпадать с соответствующим типом параметра значение (ссылки).
  • Последовательность операторов операторы задает программу вычисления значения свойства. В теле процедуры можно использовать оператор Exit Property для немедленного выхода из процедуры.

Классы, как упаковка

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

Построим класс Plan, в котором по существу есть только одно свойство, хранящее текущий месяц. Хранится это свойство, как целое, но для внешнего мира оно выглядит как обычное имя месяца. Вот как это реализовано.

'Класс Plan
'Свойство класса
Private CurMonth As Integer
'Закрытый массив, играющий служебную роль
Private Месяцы(1 To 12) As String

Private Sub Class_Initialize()
	CurMonth = Month(Now)

	Месяцы(1) = "Январь": Месяцы(2) = "Февраль": Месяцы(3) = "Март"
	Месяцы(4) = "Апрель": Месяцы(5) = "Май": Месяцы(6) = "Июнь"
	Месяцы(7) = "Июль": Месяцы(8) = "Август": Месяцы(9) = "Сентябрь"
	Месяцы(10) = "Октябрь": Месяцы(11) = "Ноябрь": Месяцы(12) = "Декабрь"
End Sub

Public Property Get ТекущийМесяц() As String
    ТекущийМесяц = Месяцы(CurMonth)
End Property

Public Property Let ТекущийМесяц(ByVal NewValue As String)
Dim i As Byte
i = 1
Do While i <= 12
    If Месяцы(i) = NewValue Then
        CurMonth = i
        Exit Do
    End If
    i = i + 1
 Loop
If i = 13 Then  'Неверно задан месяц
    CurMonth = Month(Now)
End If
End Property
4.3.

Обратите внимание, работу с закрытым свойством CurMonth обеспечивают процедуры - свойства Property Let ТекущийМесяц и Property Get ТекущийМесяц. С их помощью можно читать и изменять значение свойства CurMonth. Одновременно Let и Get занимаются преобразованием данных, что является распространенной практикой. Пример этот интересен и с позиций построения конструктора по умолчанию, который выполняет все необходимые внутренние инициализации, в данном случае, задавая значение массиву Месяцы.

Приведем еще процедуру, которая работает с данными этого класса:

Public Sub WorkWithPlan()
 Dim MyPlan As New Plan
 Debug.Print MyPlan.ТекущийМесяц
 MyPlan.ТекущийМесяц = "Апрель"
 Debug.Print MyPlan.ТекущийМесяц
 MyPlan.ТекущийМесяц = "Янв."
 Debug.Print MyPlan.ТекущийМесяц

End Sub

В результате ее работы, (процедура запускалась в марте месяце) напечатаны значения:

Март
Апрель
Март

Задумаемся над сутью решаемой задачи, - нам хочется при обращении к свойству класса получать значение текущего месяца. Но ведь текущий месяц, он и есть текущий, и пользователь не должен изменять его значение. Так что первое, что следовало бы сделать, это воспользоваться стратегией Read- only и не разрешать изменять значение свойства. Поэтому исключим Property Let из нашего класса. Но в этом примере есть куда более интересная особенность. Исключив Property Let, мы понимаем, что теперь теряет смысл хранение самого свойства CurMonth. Его значение мы можем (и должны по смыслу свойства) получать динамически, обращаясь к системной процедуре Month(Now). Удалим из класса и переменную CurMonth. Наш новый класс PlanNew стал проще:

'Класс PlanNew
'Закрытый массив, играющий служебную роль
Private Месяцы(1 To 12) As String

Private Sub Class_Initialize()
Месяцы(1) = "Январь": Месяцы(2) = "Февраль": Месяцы(3) = "Март"
Месяцы(4) = "Апрель": Месяцы(5) = "Май": Месяцы(6) = "Июнь"
Месяцы(7) = "Июль": Месяцы(8) = "Август": Месяцы(9) = "Сентябрь"
Месяцы(10) = "Октябрь": Месяцы(11) = "Ноябрь": Месяцы(12) = "Декабрь"
End Sub

Public Property Get ТекущийМесяц() As String
    ТекущийМесяц = Месяцы(Month(Now))
End Property

Свойство ТекущийМесяц теперь имеет статус Read- only. Но главная суть не в этом. Теперь отчетливо видна настоящая роль класса PlanNew, - он является красивой упаковкой вызова служебных функций Month(Now). Стоило ли создавать класс для этой цели? Не проще ли просто написать стандартную процедуру или вообще все делать самому, обращаясь к служебной функции и затем преобразуя номер месяца в его название? Для одной функции, как в нашем примере, вероятно, не стоит создавать класс, но для десятка функций это вполне целесообразно. Эта техника с особым успехом используется, когда нужно упаковать обращение к Win API функциям. Так что, подводя итог, отметим, что иногда классы используются как упаковка, расширяющая стандартные возможности и особую роль в этом процессе играют процедуры - свойства.

Семейство классов и процедуры - свойства

Когда создается семейство классов, без свойств - участников не обойтись. В классах семейства, основанных на механизме встраивания, обязательно есть свойства, представляющие собой объекты. Эти свойства, как и терминальные, обычно, закрываются и для работы с ними используются процедуры - свойства Property Set и Property Get. Иногда приходится передавать и дополнительные параметры при работе с ними. Рассмотрим типичный пример. Ранее мы построили класс " Личность ", теперь построим класс " Группа ", содержащий группу личностей. При построении этого класса ограничимся минимальными средствами, необходимыми для демонстрации работы со свойствами:

'Класс Группа Личностей
Const РазмерГруппы As Byte = 25
'Свойства
Private Group(1 To РазмерГруппы) As Личность

'Процедуры-свойства
Public Property Get ЧленГруппы(num As Byte) As Личность
    'Если номер корректен
    If (num >= 1) And (num <= РазмерГруппы) Then
        'Если существует в группе личность с таким номером
        If Not (Group(num) Is Nothing) Then
            Set ЧленГруппы = Group(num)
        Else: MsgBox ("В группе нет человека с номером " & num)
        End If
    Else: MsgBox ("Некорректно задан номер в группе - " & num)
    End If
End Property

Public Property Set ЧленГруппы(num As Byte, NewValue As Личность)
 'Если номер корректен
    If (num >= 1) And (num <= РазмерГруппы) Then
        'Если в группе нет личности с таким номером, то она создается
        If Group(num) Is Nothing Then
           Set Group(num) = NewValue
        Else: MsgBox ("В группе уже есть человек с номером " & num)
        End If
    Else: MsgBox ("Некорректно задан номер в группе - " & num)
    End If
End Property

Public Sub Сведения()
    Dim i As Byte
    For i = 1 To РазмерГруппы
        If Not (Group(i) Is Nothing) Then
            Group(i).PrintPerson
        End If
    Next i
End Sub
4.4.

Массив объектов класса " Личность " является закрытым свойством класса " Группа ". Процедуры - свойства Property Get ЧленГруппы и Property Set ЧленГруппы обеспечивают доступ к элементам этого массива. Индекс элемента является дополнительным параметром. Заметьте, процедура Set имеет статус Write-once, - если элемент с заданным номером уже определен в группе, то он не переопределяется. При создании этих процедур, нам, конечно же, пришлось модифицировать стандартные заготовки Let и Get. Для полноты картины приведем процедуру, в которой показано, как работать с группой:

Public Sub WorkWithGroup()
  	Dim UserOne As New Личность
    Dim UserTwo As New Личность
    Dim UserThree As New Личность
    Dim Знакомые As New Группа  
 Dim NewUser As New Личность
    'Личности
    UserOne.InitPerson FN:="Петр", LN:="Петров", DoB:=#1/23/1968#
    UserTwo.InitPerson FN:="Анна", LN:="Козлова", DoB:=#7/21/1968#
    UserThree.InitPerson FN:="Анна", LN:="Керн", DoB:=#5/17/1803#
    'Группа
    Set Знакомые.ЧленГруппы(1) = UserOne
    Set Знакомые.ЧленГруппы(2) = UserTwo
    Set Знакомые.ЧленГруппы(1) = UserThree
    Set Знакомые.ЧленГруппы(3) = UserThree
    Set NewUser = Знакомые.ЧленГруппы(7)
    Set NewUser = Знакомые.ЧленГруппы(3)
    Знакомые.Сведения

End Sub

Не останавливаясь на тех диалогах, которые будут появляться по ходу выполнения, приведем результаты печати сведений о наших старых знакомых:

Петр         Петров        родился       23.01.68 
Анна         Козлова       родилась      21.07.68 
Анна         Петровна      Керн          родилась      17.05.1803

Мы уже говорили о том, что классы могут выступать в роли привлекательной упаковки при работе со служебными функциями. Такую же "упаковочную" роль они могут играть и при работе со встроенными объектами. Свойствами такого класса могут быть, например, элементы управления, - списки, кнопки. Класс - упаковка позволит обращаться к методам и свойствам объекта, может быть в более привлекательной для конечного пользователя форме. Кроме того, в классе могут быть определены и дополнительные свойства и методы, расширяющие стандартные возможности.

Без процедур - свойств можно обойтись, заменив их обычными методами класса. Правда, такая замена приведет, обычно, к некоторой потере эффективности. Но методы являются основным способом работы с данными (свойствами), определяя поведение объектов класса.

< Лекция 4 || Лекция 5: 123456 || Лекция 6 >
полина есенкова
полина есенкова
Дмитрий Вологжин
Дмитрий Вологжин
Добрый день, прошел тесты с 1 по 9, 10 не сдал, стал читать лекцию и всё пройденные тесты с 1 по 9 сбросились, когда захотел пересдать 10 тест.