Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Тема-05-03испр.docx
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
376.1 Кб
Скачать

5.3.2. Средства создания классов в vb

5.3.2.1. Средства создания пользовательских классов

Пришло время создавать и использовать свои собственные классы в составе проектов, при этом необходимо понимать четкое разделение понятий класс и объект. Для определения класса используется оператор Class. Простейшее описание класса:

Class имя_класса

Тело_класса

End Class

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

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

  • методы – процедуры класса;

  • свойства – синтаксическая надстройка, позволяющая осуществлять в форме вызов функции, аналогичной чтению/записи переменной (например, можно объявить свойство Возраст, и при попытке записи в него отрицательного значения выдавать ошибку);

  • события – синтаксическая надстройка, поддерживаемая компилятором и средой VB, которая позволяет вызывать методы других объектов, подписавшихся на данное событие (например, подписавшись на событие Нажатие объекта, Кнопка, подписавшийся объект каждый раз при нажатии кнопки будет получать уведомление в виде вызова метода).

События и делегаты будут рассмотрены в Теме 5.4.

Основу любого класса составляют его конструкторы, поля и методы.

Поля класса синтаксически являются обычными переменными (объектами) языка. Их описание удовлетворяет обычным правилам объявления переменных. Содержательно поля задают представление той самой абстракции данных, которую реализует класс. Поля характеризуют свойства объектов класса. Необходимо напомнить, что когда создается новый объект класса, то этот объект представляет собой набор полей класса. Два объекта одного класса имеют один и тот же набор полей, но разнятся значениями, хранимыми в этих полях. Например, все объекты класса Person могут иметь поле, характеризующее рост персоны, но один объект может быть высокого роста, другой – низкого, а третий – среднего роста.

Каждое поле имеет модификатор (атрибут) доступа, принимающий одно из четырех значений: Public, Private, Protected, Friend. Атрибутом доступа по умолчанию является атрибут Рrivate. Независимо от значения атрибута доступа все поля доступны для всех методов данного класса. Они являются для методов класса глобальной информацией, с которой работают все методы, извлекая из полей нужные им данные и изменяя значения полей в ходе работы. Если поля доступны только для методов класса, то тогда они имеют атрибут доступа Private, который можно опускать. Такие поля считаются закрытыми.

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

Но часто желательно, чтобы некоторые поля были доступны в более широком контексте. Если некоторые поля класса A должны быть доступны для методов класса B, являющегося потомком класса A, то такие поля следует снабдить атрибутом Protected. Такие поля называются защищенными. Если некоторые поля должны быть доступны для методов классов B1, B2, и так далее, дружественных по отношению к классу A, то такие поля следует снабдить атрибутом Friend, а все дружественные классы B поместить в один проект (assembly). Такие поля называются дружественными. Наконец, если некоторые поля должны быть доступны для методов любого класса B, которому доступен сам класс A, то такие поля следует снабдить атрибутом Public. Такие поля называются общедоступными или открытыми.

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

Методы класса синтаксически являются обычными процедурами и функциями языка. Их описание удовлетворяет обычным правилам объявления процедур и функций. Содержательно методы определяют ту самую абстракцию данных, которую реализует класс. Методы описывают операции, доступные над объектами класса и определяют действия, выполняемые с экземпляром класса при обработке данных. Два объекта одного класса имеют один и тот же набор методов.

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

  • чтение, запись (Read, Write);

  • чтение, запись при первом обращении (Read, Write-once);

  • только чтение (Read-only);

  • только запись (Write-only);

  • ни чтения, ни записи (Not Read, Not Write).

Рассмотрим вначале пример, а потом уточним синтаксис этих методов. Рассмотрим класс Person, у которого пять полей: fam, status, salary, age, health, характеризующих фамилию, статус, зарплату, возраст и здоровье персоны. Для каждого из этих полей может быть разумной своя стратегия доступа. Возраст доступен для чтения и записи, фамилию можно задать только один раз, статус можно только читать, зарплата недоступна для чтения, а здоровье закрыто для доступа, только специальные методы класса могут сообщать некоторую информацию о здоровье персоны. В следующем примере показано, как на VB можно обеспечить эти стратегии доступа к закрытым полям класса, при этом в классе используется перечисление Enum, которое представляет собой набор связанных констант, т.е. изменять их значения нельзя, а для доступа к конкретной константе используется точечная нотация:

'Класс, задающий общие свойства и поведение личности

Public Class Person

Public Enum Status1'перечисление всех возможных значений

ребенок 'объекта Status1

школьник

студент

работник

пенсионер

End Enum

'поля класса(все закрыты)

Dim fam As String = ""

Dim health As String = ""

Dim age As Integer = 0

Dim salary As Integer = 0

Dim status As Status1 = Status1.работник

'методы - свойства

'стратегия: Read,Write-once (Чтение,запись при первом обращении)

Public Property Fam1() As String

Set(ByVal value As String)

If (fam = "") Then fam = value

End Set

Get

Return fam

End Get

End Property

'стратегия: Read-only(Только чтение)

Public ReadOnly Property GetStatus() As Status1

Get

Return status

End Get

End Property

'стратегия: Read,Write (Чтение, запись)

Public Property Age1() As Integer

Set(ByVal value As Integer)

age = value

If (age < 7) Then

status = "ребенок"

ElseIf (age < 17) Then

status = "школьник"

ElseIf (age < 22) Then

status = "студент"

Else

status = "служащий"

End If

End Set

Get

Return age

End Get

End Property

стратегия: Write-only (Только запись)

Public WriteOnly Property Salary1() As Integer

Set(ByVal value As Integer)

salary = value

End Set

End Property

End Class

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

  • предотвращается несанкционированный доступ к данным;

  • гарантируется целостность данных путем контроля ошибок;

  • создаются свойства, доступные только для чтения и только для записи;

  • пользователь лишается возможности вносить изменения в выполняемую

программу.

Итак, данные и методы, составляющие класс (члены класса), а также сам класс могут иметь так называемые модификаторы доступа, которые указывают область их видимости. Значения модификаторов могут быть следующими:

  • Public – открытый класс или член класса, доступ к которому разрешен из любого места программного кода;

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

  • Friend – класс или член класса, доступный только внутри той же сборки, в которой объявлен (в VB сборка обычно соответствует всей программе, поэтому данный модификатор можно воспринимать как указание видимости только в пределах программы);

  • Protected – член, доступный только из самого класса и из наследующих клас­сов (данный модификатор применим только к членам классов);

  • Protected Friend – объединение областей видимости Protected и Friend.

Наряду с модификаторами доступа, регламентирующими видимость, члены класса могут содержать модификаторы, устанавливающие их принадлежность к классу или к экземпляру класса. Члены класса, принадлежащие всему классу, называют разделяемыми (Shared) или статическими членами. Разделяемыми (статическими) членами являются свойства, процедуры и поля, которые разделяются всеми объектами класса, то есть являются общими для всех объектов класса. Члены, которые принадлежат конкретному экземпляру класса, а не всему классу, называются экземплярными (Instance). Чтобы понять разницу между этими видами методов и полей (и как следствие – свойств и событий), необходимо более подробно рассмотреть механизм вызова методов и обращений к полям.

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

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

Напомним, что классы библиотеки .Net Framework использовались двояко: без создания объектов и с созданием объекта. В первом случае, чтобы воспользоваться возможностями класса, необходимо было написать имя класса, точку и имя необходимого свойства или метода этого класса. Так использовались математические функции класса System.Math. Во втором случае, чтобы воспользоваться возможностями класса, необходимо сначала создать объект – экземпляр класса, а уж затем писать имя объекта. Так использовались потоковый ввод/вывод и классы StringBuilder и Array.

При создании собственного класса, можно указывать, каким из двух способов заданный класс будет использоваться.

В проекте VS новый класс создается аналогично стандартному модулю. То есть, чтобы создать в проекте новый класс, необходимо щелкнуть на кнопке Добавление нового элемента (Add New Item) стандартной панели инструментов или на команде Добавить новый элемент… (Add New Item…) элемента главного меню Проект (Project). Также можно использовать команду Добавить класс… (Add Class…) элемента главного меню Проект (Project). В результате выполнения одной из этих команд откроется диалоговое окно Добавление нового элемента … (Add New Item…) , в котором следует выб­рать шаблон Class и указать имя класса.

После нажатия на кнопку Добавить (Add) в окне Редактор кода (Code Editor) по­явится новый пустой класс:

Public Class1

End Class

Класс проекта, созданный первым, по умолчанию имеет имя Class1.vb, но это имя можно изменить в окне Добавление нового элемента … (Add New Item…) или, сделав щелчок пра­вой кнопкой мыши на модуле класса, в окне Обозреватель решений (Solution Explorer), ввести новое имя класса.

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

В первом случае объекты создаются одновременно с их объявлением, которое имеет вид:

Dim ИмяПеременной As New ИмяКласса

Во втором случае описание переменной не связано с созданием объекта и имеет вид:

Dim ИмяПеременной As ИмяКласса,

Объект создается позже в процессе выполнения программы путем выполнения присваивания: ИмяПеременной = New ИмяКласса

Пример 5.3-1. Описать простой базовый класс.

Простой базовый класс, приведенный на рис. 5.3-1.

Public Class SampleBase ‘Базовый класс

'набор полей с различными модификаторами доступа

Private privateVar As Integer

Public publicVar As Integer

Friend friendVar As Integer

Protected protVar As Integer

'Открытый экземплярный метод класса

Public Sub SomeMethod()

privateVar = 5 ' Внутри класса доступны все его поля

End Sub

'Закрытый экземплярный метод класса

Private Function SomeOtherMethod() As Integer

Return publicVar ' Открытые поля доступны всегда

End Function

'Открытое экземплярное свойство

Public Property SomeProperty() As Integer

Get

Return privateVar

End Get

Set(ByVal Value As Integer)

privateVar = Value

End Set

End Property

'Открытый статический метод класса

Public Shared Sub SharedMethod()

End Sub

End Class

Public Class Form1

. . .

Точка входа в программу

Private Sub Button1_Click( )

Dim a As New SampleBase

a.friendVar = 5 ‘Допустимо: обращение из той же программы a.publicVar = 6 ‘Допустимо: поле открыто

a.privateVar = 6 ‘Ошибка: поле класса закрыто

а.SomeProperty = 5

SampleBase.SharedMethod()'обращение к статическому методу: вместо

End Sub 'имени объекта указывается имя класса

End Class

Рис. 5.3-1

В данном примере создается класс SampleBase. В классе SampleBase объявлен набор полей, методов и одно свой­ство. При попытке откомпилировать этот код будут выдано сообщение:

'sampleVBProject.SampleBase.privateVar'is not accessible in thiscontext because it is 'Private'.

Это показывает, что закрытые (Private) члены не доступны вне контекста класса.

Обратите внимание на имя переменной в сообщении об ошибке:

sampleVBProject.SampleBase.privateVar

Оно представляет собой полное имя члена класса, которое состоит из пространства имен (sampleVBProject), имени класса (SampleBase), имени члена (privateVar).

Рассмотрим что такое New. Это – конструктор, представляющий собой специальный метод класса, позволяющий создавать объекты класса. Конструктор - неотъемлемый компонент класса. Нет классов без конструкторов. Одна из синтаксических особенностей этого метода в том, что его имя должно совпадать с именем класса. Если программист не определяет конструктор класса, то к классу автоматически добавляется конструктор по умолчанию – конструктор без аргументов. Заметьте, если программист сам создает один или несколько конструкторов, то автоматического добавления конструктора без аргументов не происходит.

Как и когда происходит создание объектов? Чаще всего, при объявлении сущности в момент ее инициализации. Обратимся, к примеру, который описывает класс Person, и рассмотрим создание двух объектов класса Person:

Dim pers1 As New Person()

Dim pers2 As New Person("Петрова")

Объекты pers1, pers2 класса Person объявляются с инициализацией, задаваемой унарной операцией New, которой в качестве аргумента передается конструктор класса Person. У класса может быть несколько конструкторов – это типичная практика, – отличающихся сигнатурой (т.е. отличающихся количеством и типом параметров). В данном примере в первой строке вызывается конструктор без аргументов, а во второй строке для сущности pers2 вызывается конструктор с одним аргументом типа String. Разберем в деталях процесс создания:

Первым делом для объектов pers1 и pers2 создаются ссылки, пока висячие со значением null.

  • Затем в динамической памяти создается объект – структура данных с полями, определяемыми классом Person. Поля объекта инициализируются значениями по умолчанию: ссылочные поля – значением null, арифметические – нулями, строковые – пустой строкой. Эту работу выполняет конструктор по умолчанию, который можно считать всегда вызывается в начале процесса создания. Если поля класса проинициализированы, как в нашем примере, то выполняется инициализация полей заданными значениями.

  • Если вызван конструктор с аргументами, то начинает выполняться тело этого конструктора. Как правило, при этом происходит инициализация отдельных полей класса значениями, переданными конструктору. Так поле fam объекта pers2 получает значение «Петрова».

  • На заключительном этапе ссылка связывается с созданным объектом.

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

Зачем классу нужно несколько конструкторов? Дело в том, что в зависимости от контекста и создаваемого объекта может требоваться различная инициализация его полей.

Перегрузка конструкторов и обеспечивает решение этой задачи.

Конструктор может быть объявлен с атрибутом Private. Понятно, что в этом случае внешний пользователь не может воспользоваться им для создания объектов. Но это могут делать методы класса, создавая объекты для собственных нужд со специальной инициализацией.

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

Shared Sub Person()

Console.WriteLine("Выполняется статический конструктор!");

End Sub

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

Если задача создания объектов полностью возлагается на программиста, то задача удаления объектов, после того, как они стали ненужными, в VS .Net снята с программиста и возложена на соответствующий инструментарий VS – сборщик мусора. В классическом варианте языка C++ деструктор также необходим классу, как и конструктор. В языке VB y класса может быть деструктор, но он не занимается удалением объектов и не вызывается нормальным образом в ходе выполнения программы. Так же, как и статический конструктор, деструктор класса, если он есть, вызывается автоматически в процессе сборки мусора. Его роль в освобождении ресурсов, например файлов, открытых объектом.

Имя деструктора строится из имени класса с предшествующим ему символом ~ (тильда). Как и у статического конструктора, у деструктора не указывается модификатор доступа.

Заметьте, правильный стиль требует, чтобы среди конструкторов класса всегда присутствовал конструктор по умолчанию - конструктор без аргументов. Иногда тело этого конструктора пусто, поскольку все поля объявлены с инициализацией и конструктору нет смысла изменять эти значения. Часто имена формальных аргументов конструктора совпадают с соответствующими именами полей класса. Поэтому при присваивании полю объекта значения аргумента необходимо уточнить имя поля именем объекта. В данном случае речь идет о текущем объекте, имя которого фиксировано - Me.

В общем виде описание класса будет следующим:

[ <attributelist> ] [ accessmodifier ] [ Shadows ] _

[ MustInherit | NotInheritable ] [ Partial ] _

Class name [ ( Of typelist ) ]

[ Inherits classname ]

[ Implements interfacenames ]

[ statements ]

End Class

Термин

Определение

attributelist

Необязательный. См. Список атрибутов.

accessmodifier

Необязательный. Может принимать следующие значения:

Public

Защищенный

Friend

Закрытый

Protected Friend

Уровни доступа в Visual Basic.

Shadows

Необязательный

MustInherit

Необязательный..

NotInheritable

Необязательный.

Partial

Необязательный. Указывает частичное определение класса.

name

Обязательный. Имя этого класса.

Of

Необязательный. Указывает на принадлежность к универсальному классу.

typelist

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

Inherits

Необязательный. Указывает на то, что данный класс наследует члены другого класса.

classname

Является обязательным, если используется оператор Inherits. Имя класса, от которого наследует данный класс.

Implements

Необязательный. Указывает на то, что этот класс реализует члены одного или нескольких интерфейсов.

interfacenames

Является обязательным, если используется оператор Implements. Имена интерфейсов, реализуемых данным классом.

statements

Необязательный. Операторы, определяющие члены этого класса.

End Class

Обязательный. Завершает определение Class.

Оператор Class определяет новый тип данных. Класс является основным строительным блоком в объектно-ориентированном программировании (OOП).

Оператор Class можно использовать только на уровне пространства имен или модуля. Это означает, что контекстом объявления для класса должен быть исходный файл, пространство имен, класс, структура, модуль или интерфейс и не может быть процедура или блок.

Каждый экземпляр класса имеет время жизни, не зависящее от всех других экземпляров. Это время жизни начинается при создании экземпляра (оператор New) . Оно заканчивается, когда все переменные, указывающие на экземпляр, были установлены в Nothing или в экземпляры других классов.

Классы по умолчанию имеют доступ Friend. Уровни доступа можно настроить с помощью модификаторов доступа.

Правила:

  • Вложения. Можно определить один класс внутри другого. Внешний класс называется содержащим классом, а внутренний класс – вложенным классом.

  • Наследование. Если класс использует Инструкция Inherits, то можно указать только один базовый класс или интерфейс. Класс не может наследовать от нескольких элементов.

Класс не может наследовать от другого класса с более строгим уровнем доступа. Например, класс Public не может наследовать от класса Friend.

Класс не может наследовать от вложенного в него класса.

  • Реализация. Если класс использует Оператор Implements, то необходимо реализовать каждый член, определенный каждым интерфейсом в interfacenames. Исключение составляет повторная реализация члена базового класса.

Поведение:

  • Уровень доступа. В классе можно объявить каждый член со своим собственным уровнем доступа. Члены класса по умолчанию имеют доступ Public, за исключением переменных и констант, которые по умолчанию имеют доступ Private. Если класс имеет более ограниченный доступ, чем один из его членов, уровень доступа к заданному классу имеет больший приоритет.

  • Область действия класса – его пространство имен, класс, структура или модуль.

  • Областью действия каждого члена класса является весь класс.

  • Время существования. VB не поддерживает статические классы. Функциональный эквивалент статического класса обеспечивается модулем.

Время существования членов класса зависит от того, как и где они были объявлены.

  • Квалификация. Код вне класса должен предварять имя члена именем этого класса.

Если код внутри вложенного класса делает неполную ссылку на элемент программирования, VB ищет элемент сначала во вложенном классе, затем в содержащем его классе, и так далее до выхода из внешнего содержащего элемента.