3. Класи, структури, інтерфейси
3.1. Об’єкт на прикладі
3.1.1. Загальні відомості
Тепер на прикладі розглянемо об’єктне програмування. Об’єкт — це суть, що містить дані, які визначають його стан (їх називають властивостями), та функції (їх називають методами). Об’єкт створюється за зразком, який називається класом:
Public Class c1
' атрибути
Private p1 As type1
Private p2 As type2
....
' метод
Public Sub m1(....)
...
End Sub
' метод
Public Function m2(...)
....
End Function
End Class
На основі попереднього класу C1 можна створити багато об’єктів O1, O2,… Усі вони матимуть властивості p1, p2,… та методи m3, m4,… Але вони матимуть різні значення для своїх властивостей pi, отже кожен з них матиме власний стан. За аналогією оголошення
створює два об’єкти (термін тут вжито неправильно) типу (класу) Integer. Їхньою єдиною властивістю є їхнє значення. Якщо O1 є об’єктом типу C1, то O1.p1 позначає властивість p1 об’єкта O1, а O1.m1 — метод m1 об’єкта O1. Розглянемо першу модель об’єкта: клас personne.
3.1.2. Визначення класу «особа»
Визначення класу personne буде таким:
Public Class personne
' атрибути
Private prenom As String
Private nom As String
Private age As Integer
' метод
Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
Me.prenom = P
Me.nom = N
Me.age = age
End Sub
' метод
Public Sub identifie()
Console.Out.WriteLine((prenom & "," & nom & "," & age))
End Sub
End Class
Тут ми маємо визначення класу, тобто типу даних. Коли ми будемо створювати змінні цього типу, їх називатимуть об’єктами або екземплярами класів. Отже, клас — це шаблон, на основі якого створюються об’єкти. Елементами або полями класу можуть бути дані (атрибути), методи (функції) та властивості. Властивості — це особливі методи, що слугують для отримання або встановлення значень атрибутів об’єкта. Ці поля можуть супроводжуватися одним із трьох таких ключових слів:
Приватне поле (private) доступне лише для внутрішніх методів класу | |
До публічного поля (public) мають доступ усі функції, незалежно від того, чи визначені вони в межах класу | |
До захищеного поля (protected) мають доступ лише внутрішні методи класу або похідного об’єкта (див. далі поняття успадкування). |
Зазвичай дані класу оголошуються приватними, тоді як його методи та властивості — публічними. Це означає, що користувач об’єкта (програміст):
- не матиме прямого доступу до приватних даних об’єкта
- може викликати публічні методи об’єкта, зокрема ті, що надають доступ до його приватних даних.
Синтаксис оголошення класу такий:
public class classe
private donnée ou méthode ou propriété privée
public donnée ou méthode ou propriété publique
protected donnée ou méthode ou propriété protégée
end class
Порядок зазначення атрибутів private, protected та public може бути будь-яким.
3.1.3. Метод ініціалізує
Повернемося до нашого класу [personne], оголошеного так:
Public Class personne
' атрибути
Private prenom As String
Private nom As String
Private age As Integer
' метод
Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
Me.prenom = P
Me.nom = N
Me.age = age
End Sub
' метод
Public Sub identifie()
Console.Out.WriteLine((prenom & "," & nom & "," & age))
End Sub
End Class
Яку роль відіграє метод initialise? Оскільки nom, prenom та age є приватними даними класу personne, оператори:
є недопустимими. Нам потрібно ініціалізувати об’єкт типу personne за допомогою публічного методу. Цю роль виконує метод initialise. Ми напишемо:
Запис p1.initialise є допустимим, оскільки initialise має публічний доступ.
3.1.4. Оператор new
Послідовність команд
є неправильною. Команда
оголошує p1 як посилання на об’єкт типу personne. Цей об’єкт ще не існує, а отже p1 не ініціалізовано. Це так, ніби ми написали:
де за допомогою ключового слова nothing явно вказується, що змінна p1 ще не посилається на жоден об’єкт. Коли потім записуємо
, ми викликаємо метод initialise об’єкта, на який посилається p1. Однак цей об’єкт ще не існує, і компілятор повідомить про помилку. Щоб p1 посилався на об’єкт, потрібно написати:
Це призведе до створення ще не ініціалізованого об’єкта типу personne: атрибути nom та prenom, які є посиланнями на об’єкти типу String, матимуть значення nothing, а age — значення 0. Отже, відбувається ініціалізація за замовчуванням. Тепер, коли p1 посилається на об’єкт, інструкція ініціалізації цього об’єкта
є дійсною.
3.1.5. Ключове слово Me
Розглянемо код методу initialise:
Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
Me.prenom = P
Me.nom = N
Me.age = age
End Sub
Інструкція Me.prenom=P означає, що атрибут prenom поточного об’єкта (Me) отримує значення P. Ключове слово Me позначає поточний об’єкт: той, у якому знаходиться метод, що виконується. Звідки ми це знаємо? Давайте подивимося, як відбувається ініціалізація об’єкта, на який вказує p1, у програмі, що викликає:
Викликається метод initialise об’єкта p1. Коли в цьому методі згадується об’єкт Me, насправді мається на увазі об’єкт p1. Метод initialise також можна було б написати так:
Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
prenom = P
nom = N
Me.age = age
End Sub
Коли метод об'єкта посилається на атрибут A цього об'єкта, запис Me.A є неявним. Його слід використовувати явно, коли виникає конфлікт ідентифікаторів. Це стосується такої інструкції:
Me.age=age;
де age позначає атрибут поточного об’єкта, а також параметр age, отриманий методом. У такому разі необхідно усунути неоднозначність, позначивши атрибут age як Me.age.
3.1.6. Тестова програма
Ось коротка тестова програма:
Public Class personne
' атрибути
Private prenom As String
Private nom As String
Private age As Integer
' метод
Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
Me.prenom = P
Me.nom = N
Me.age = age
End Sub
' метод
Public Sub identifie()
Console.Out.WriteLine((prenom & "," & nom & "," & age))
End Sub
End Class
та отримані результати:
dos>vbc personne1.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
3.1.7. Використання файлу скомпільованих класів (асемблера)
Зверніть увагу, що в попередньому прикладі у нашій тестовій програмі є два класи: класи personne та test1. Існує й інший спосіб:
-
компілюємо клас «personne» в окремий файл, який називається збіркою (assembly). Цей файл має розширення .dll
-
компілюємо клас test1, посилаючись на збірку, що містить клас «person».
Обидва вихідні файли мають такий вигляд:
test.vb | |
personne2.vb | |
Клас personne компілюється за допомогою такої інструкції:
dos>vbc /t:library personne2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
24/02/2004 16:50 509 personne2.vb
24/02/2004 16:49 143 test.vb
24/02/2004 16:50 3 584 personne2.dll
В результаті компіляції було створено файл personne2.dll. Саме опція компіляції /t:library вказує на створення файлу «assembly». Тепер скомпілюємо файл test.vb:
dos>vbc /r:personne2.dll test.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
24/02/2004 16:50 509 personne2.vb
24/02/2004 16:49 143 test.vb
24/02/2004 16:50 3 584 personne2.dll
24/02/2004 16:51 3 072 test.exe
Параметр компіляції /r:personne2.dll вказує компілятору, що певні класи містяться у файлі personne2.dll. Коли у вихідному файлі test.vb він знайде посилання на клас personne, який не оголошено у вихідному файлі test.vb, він шукатиме клас personne у файлах .dll, на які посилається опція /r. Тут він знайде клас personne у збірці personne2.dll. У цей збірник можна було б додати й інші класи. Щоб під час компіляції використовувати кілька файлів скомпільованих класів, слід написати:
Виконання програми test1.exe дає такі результати:
3.1.8. Інший метод ініціалізує
Розглянемо знову клас personne і додамо до нього такий метод:
' метод
Public Sub initialise(ByVal P As personne)
prenom = P.prenom
nom = P.nom
Me.age = P.age
End Sub
Тепер ми маємо два методи з назвою initialise: це допустимо, якщо вони приймають різні параметри. У даному випадку це саме так. Параметром тепер є посилання P на особу. Атрибути особи P при цьому присвоюються поточному об’єкту (Me). Слід зауважити, що метод initialise має прямий доступ до атрибутів об’єкта P, хоча вони мають тип private. Це завжди так: об’єкт O1 класу C завжди має доступ до атрибутів об’єктів того самого класу C. Ось тест нового класу personne, який було скомпільовано в personne.dll, як пояснювалося раніше:
' параметри
Option Explicit On
Option Strict On
' простори імен
Imports System
Module test1
Sub Main()
Dim p1 As New personne
p1.initialise("Jean", "Dupont", 30)
Console.Out.Write("p1=")
p1.identifie()
Dim p2 As New personne
p2.initialise(p1)
Console.Out.Write("p2=")
p2.identifie()
End Sub
End Module
та його результати:
3.1.9. Конструктори класу «особа»
Конструктор — це процедура з іменем New, яка викликається під час створення об’єкта. Зазвичай її використовують для його ініціалізації. Якщо клас має конструктор, що приймає n аргументів argi, оголошення та ініціалізація об’єкта цього класу можуть здійснюватися у такій формі:
dim objet as classe =new classe(arg1,arg2, ... argn)
або
dim objet as classe
…
objet=new classe(arg1,arg2, ... argn)
Якщо клас має один або кілька конструкторів, для створення об’єкта цього класу обов’язково має бути використаний один із цих конструкторів. Якщо клас C не має жодного конструктора, він має конструктор за замовчуванням — конструктор без параметрів: public New(). Атрибути об’єкта тоді ініціалізуються значеннями за замовчуванням. Саме це відбувалося в попередніх програмах, де ми писали:
Створимо два конструктори для нашого класу personne:
' параметри
Option Explicit On
Option Strict On
' простори імен
Imports System
' клас «особа»
Public Class personne
' атрибути
Private prenom As String
Private nom As String
Private age As Integer
' конструктори
Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
initialise(P, N, age)
End Sub
Public Sub New(ByVal P As personne)
initialise(P)
End Sub
' методи ініціалізації об’єкта
Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
Me.prenom = P
Me.nom = N
Me.age = age
End Sub
Public Sub initialise(ByVal P As personne)
prenom = P.prenom
nom = P.nom
Me.age = P.age
End Sub
' метод
Public Sub identifie()
Console.Out.WriteLine((prenom & "," & nom & "," & age))
End Sub
End Class
Наші два конструктори просто викликають відповідні методи initialise. Нагадаємо, що коли в конструкторі зустрічається, наприклад, запис initialise(P), компілятор перетворює його на Me.initialise(P). Отже, у конструкторі викликається метод initialise для роботи з об’єктом, на який посилається Me, тобто з поточним об’єктом, який саме створюється. Ось коротка тестова програма:
' опції
Option Explicit On
Option Strict On
' простори імен
Imports System
' тестова сторінка
Module test
Sub Main()
Dim p1 As New personne("Jean", "Dupont", 30)
Console.Out.Write("p1=")
p1.identifie()
Dim p2 As New personne(p1)
Console.Out.Write("p2=")
p2.identifie()
End Sub
End Module
та отримані результати:
3.1.10. Посилання на об’єкти
Ми як і раніше використовуємо той самий клас personne. Тестова програма виглядає наступним чином:
' параметри
Option Explicit On
Option Strict On
' простори імен
Imports System
' тестова сторінка
Module test
Sub Main()
' p1
Dim p1 As New personne("Jean", "Dupont", 30)
Console.Out.Write("p1=")
p1.identifie()
' p2 посилається на той самий об’єкт, що й p1
Dim p2 As personne = p1
Console.Out.Write("p2=")
p2.identifie()
' p3 посилається на об’єкт, який буде копією об’єкта, на який посилається p1
Dim p3 As New personne(p1)
Console.Out.Write("p3=")
p3.identifie()
' змінюємо стан об’єкта, на який посилається p1
p1.initialise("Micheline", "Benoît", 67)
Console.Out.Write("p1=")
p1.identifie()
' оскільки p2=p1, стан об’єкта, на який посилається p2, мав змінитися
Console.Out.Write("p2=")
p2.identifie()
' оскільки p3 не посилається на той самий об’єкт, що й p1, об’єкт, на який посилається p3, не мав змінитися
Console.Out.Write("p3=")
p3.identifie()
End Sub
End Module
Отримані результати такі:
p1=Jean,Dupont,30
p2=Jean,Dupont,30
p3=Jean,Dupont,30
p1=Micheline,Benoît,67
p2=Micheline,Benoît,67
p3=Jean,Dupont,30
Коли оголошується змінна p1 за допомогою
p1 посилається на об’єкт personne("Jean","Dupont",30), але не є самим об’єктом. У мові C це можна порівняти з покажчиком — c.a.d — адресою створеного об’єкта. Якщо потім написати:
Змінюється не сам об’єкт personne("Jean","Dupont",30), а значення посилання p1. Об’єкт personne("Jean","Dupont",30) буде «втрачено», якщо на нього не посилається жодна інша змінна.
Коли ми пишемо:
ініціалізується покажчик p2: він «вказує» на той самий об’єкт (позначає той самий об’єкт), що й покажчик p1. Отже, якщо змінити об’єкт, на який «вказує» (або на який посилається) p1, то зміниться й об’єкт, на який посилається p2.
Коли ми пишемо:
створюється новий об’єкт — копія об’єкта, на який посилається p1. На цей новий об’єкт буде посилатися p3. Якщо змінити об’єкт, на який «вказує» (або на який посилається) p1, це жодним чином не вплине на об’єкт, на який посилається p3. Саме це показують отримані результати.
3.1.11. Тимчасові об’єкти
У виразі можна явно викликати конструктор об’єкта: об’єкт створюється, але ми не маємо до нього доступу (наприклад, щоб його змінити). Цей тимчасовий об’єкт створюється для обчислення виразу, а потім знищується. Пам’ять, яку він займав, згодом автоматично звільняється програмою, яка називається «збирач сміття» (garbage collector), завданням якої є звільнення пам’яті, зайнятої об’єктами, на які більше не посилаються дані програми. Розглянемо таку нову тестову програму:
' опції
Option Strict On
Option Explicit On
' простори імен
Imports System
' тестова сторінка
Module test
Sub Main()
Dim p As New personne(New personne("Jean", "Dupont", 30))
p.identifie()
End Sub
End Module
і змінимо конструктори класу personne так, щоб вони виводили повідомлення:
' конструктори
Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
Console.Out.WriteLine("Constructeur personne(String, String, integer)")
initialise(P, N, age)
End Sub
Public Sub New(ByVal P As personne)
Console.Out.WriteLine("Constructeur personne(personne)")
initialise(P)
End Sub
Ми отримуємо такі результати:
dos>test
Constructeur personne(String, String, integer)
Constructeur personne(personne)
Jean,Dupont,30
що ілюструє послідовне створення двох тимчасових об’єктів.
3.1.12. Методи читання та запису приватних атрибутів
Ми додаємо до класу personne методи, необхідні для читання або зміни стану атрибутів об’єктів:
Imports System
Public Class personne
' атрибути
Private prenom As [String]
Private nom As [String]
Private age As Integer
' конструктори
Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
Me.prenom = P
Me.nom = N
Me.age = age
End Sub
Public Sub New(ByVal P As personne)
Me.prenom = P.prenom
Me.nom = P.nom
Me.age = P.age
End Sub
' ідентифікатори
Public Sub identifie()
Console.Out.WriteLine((prenom + "," + nom + "," + age))
End Sub
' методи доступу
Public Function getPrenom() As [String]
Return prenom
End Function
Public Function getNom() As [String]
Return nom
End Function
Public Function getAge() As Integer
Return age
End Function
'модифікатори
Public Sub setPrenom(ByVal P As [String])
Me.prenom = P
End Sub
Public Sub setNom(ByVal N As [String])
Me.nom = N
End Sub
Public Sub setAge(ByVal age As Integer)
Me.age = age
End Sub
End Class
Ми тестуємо новий клас за допомогою такої програми:
' опції
Option Strict On
Option Explicit On
' простори імен
Imports System
' тестова сторінка
Public Module test
Sub Main()
Dim P As New personne("Jean", "Michelin", 34)
Console.Out.WriteLine(("P=(" & P.getPrenom() & "," & P.getNom() & "," & P.getAge() & ")"))
P.setAge(56)
Console.Out.WriteLine(("P=(" & P.getPrenom() & "," & P.getNom() & "," & P.getAge() & ")"))
End Sub
End Module
і отримуємо такі результати:
3.1.13. Властивості
Існує ще один спосіб отримати доступ до атрибутів класу — це створення властивостей. Вони дозволяють нам оперувати приватними атрибутами так, ніби вони є публічними. Розглянемо наступний клас personne, у якому попередні методи доступу та модифікатори були замінені властивостями для читання та запису:
' опції
Option Explicit On
Option Strict On
' простори імен
Imports System
' клас «особа»
Public Class personne
' атрибути
Private _prenom As [String]
Private _nom As [String]
Private _age As Integer
' виробники
Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
Me._prenom = P
Me._nom = N
Me._age = age
End Sub
Public Sub New(ByVal P As personne)
Me._prenom = P._prenom
Me._nom = P._nom
Me._age = P._age
End Sub
' ідентифікатор
Public Sub identifie()
Console.Out.WriteLine((_prenom & "," & _nom & "," & _age))
End Sub
' властивості
Public Property prenom() As String
Get
Return _prenom
End Get
Set(ByVal Value As String)
_prenom = Value
End Set
End Property
Public Property nom() As String
Get
Return _nom
End Get
Set(ByVal Value As String)
_nom = Value
End Set
End Property
Public Property age() As Integer
Get
Return _age
End Get
Set(ByVal Value As Integer)
' вік у межах норми?
If Value >= 0 Then
_age = Value
Else
Throw New Exception("âge (" & Value & ") invalide")
End If
End Set
End Property
End Class
Властивість (Property) дозволяє зчитувати (get) або встановлювати (set) значення атрибута. У нашому прикладі ми додали префікс _ до імен атрибутів, щоб властивості мали ті самі імена, що й примітивні атрибути. Адже властивість не може мати того самого імені, що й атрибут, яким вона керує, оскільки в такому разі у класі виникає конфлікт імен. Тому ми назвали наші атрибути _prenom, _nom, _age і відповідно змінили конструктори та методи. Потім ми створили три властивості: nom, prenom та age. Властивість оголошується наступним чином:
Public Property nom() As Type
Get
...
End Get
Set(ByVal Value As Type)
...
End Set
End Property
де Type має бути типом атрибута, яким керує властивість. Вона може мати два методи, що називаються get та set. Метод get зазвичай відповідає за повернення значення атрибута, яким він керує (він може повертати й щось інше, ніщо цьому не заважає). Метод set отримує параметр value, який зазвичай присвоює атрибуту, яким він керує. Він може скористатися цією нагодою, щоб перевірити правильність отриманого значення і, за необхідності, викинути виняток, якщо значення виявиться недійсним. Саме це робиться тут для віку.
Як викликаються ці методи get та set? Розглянемо таку тестову програму:
' параметри
Option Explicit On
Option Strict On
' простори імен
Imports System
' тестова сторінка
Module test
Sub Main()
Dim P As New personne("Jean", "Michelin", 34)
Console.Out.WriteLine(("P=(" & P.prenom & "," & P.nom & "," & P.age & ")"))
P.age = 56
Console.Out.WriteLine(("P=(" & P.prenom & "," & P.nom & "," & P.age & ")"))
Try
P.age = -4
Catch ex As Exception
Console.Error.WriteLine(ex.Message)
End Try
End Sub
End Module
В операторі
ми намагаємося отримати значення властивостей prenom, nom та age особи P. У цьому випадку викликається метод get цих властивостей, який повертає значення атрибута, яким вони керують.
У операторі
ми хочемо встановити значення властивості age. У цьому випадку викликається метод set цієї властивості. Він отримає значення 56 у параметрі value.
Властивість P класу C, яка визначає лише метод get, називається властивістю «тільки для читання». Якщо c є об’єктом класу C, операція c.P=значення буде відхилена компілятором.
Виконання попередньої тестової програми дає такі результати:
Отже, властивості дозволяють нам оперувати приватними атрибутами так, ніби вони є публічними.
3.1.14. Методи та атрибути класу
Припустимо, що ми хочемо підрахувати кількість об’єктів [personne], створених у додатку. Можна самостійно вести лічильник, але є ризик пропустити тимчасові об’єкти, що створюються то тут, то там. Набагато надійніше було б включити в конструктори класу [personne] інструкцію, яка збільшує лічильник. Проблема полягає в тому, щоб передати посилання на цей лічильник, щоб конструктор міг його інкрементувати: для цього потрібно передати їм новий параметр. Також можна включити лічильник у визначення класу. Оскільки це атрибут самого класу, а не конкретного об’єкта цього класу, його оголошують інакше за допомогою ключового слова Shared:
Щоб посилатися на нього, пишемо personne._nbPersonnes, щоб показати, що це атрибут самого класу personne. Тут ми створили приватний атрибут, до якого не буде прямого доступу поза межами класу. Тому ми створюємо публічну властивість, щоб надати доступ до атрибута класу nbPersonnes. Щоб встановити значення nbPersonnes, метод get цієї властивості не потребує конкретного об’єкта personne: адже _nbPersonnes не є атрибутом конкретного об’єкта, а є атрибутом цілого класу. Тому нам потрібна властивість, яка також має бути оголошена як Shared:
яка зовні буде викликатися за синтаксисом personne.nbPersonnes. Властивість оголошується як доступна тільки для читання (ReadOnly), оскільки вона не має методу set. Ось приклад. Клас personne набуває такого вигляду:
Option Explicit On
Option Strict On
' простори імен
Imports System
' клас
Public Class personne
' атрибути класу
Private Shared _nbPersonnes As Long = 0
' атрибути екземпляра
Private _prenom As [String]
Private _nom As [String]
Private _age As Integer
' конструктори
Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
' ще одна людина
_nbPersonnes += 1
Me._prenom = P
Me._nom = N
Me._age = age
End Sub
Public Sub New(ByVal P As personne)
' ще одна людина
_nbPersonnes += 1
Me._prenom = P._prenom
Me._nom = P._nom
Me._age = P._age
End Sub
' ідентифікатор
Public Sub identifie()
Console.Out.WriteLine((_prenom & "," & _nom & "," & _age))
End Sub
' властивість класу
Public Shared ReadOnly Property nbPersonnes() As Long
Get
Return _nbPersonnes
End Get
End Property
' властивості екземпляра
Public Property prenom() As String
Get
Return _prenom
End Get
Set(ByVal Value As String)
_prenom = Value
End Set
End Property
Public Property nom() As String
Get
Return _nom
End Get
Set(ByVal Value As String)
_nom = Value
End Set
End Property
Public Property age() As Integer
Get
Return _age
End Get
Set(ByVal Value As Integer)
' вік у межах норми?
If Value >= 0 Then
_age = Value
Else
Throw New Exception("âge (" & Value & ") invalide")
End If
End Set
End Property
End Class
З такою програмою:
' параметри
Option Explicit On
Option Strict On
' простори імен
Imports System
' тестова сторінка
Module test
Sub Main()
Dim p1 As New personne("Jean", "Dupont", 30)
Dim p2 As New personne(p1)
Console.Out.WriteLine(("Nombre de personnes créées : " & personne.nbPersonnes))
End Sub
End Module
отримуємо такі результати:
3.1.15. Передача об’єкта у функцію
Ми вже зазначали, що за замовчуванням VB.NET передає фактичні параметри функції за значенням: значення фактичних параметрів копіюються у формальні параметри. У випадку з об’єктом не слід давати себе ввести в оману систематичним неправильним вживанням терміна «об’єкт» замість «посилання на об’єкт». Об’єкт обробляється виключно через посилання (покажчик) на нього. Отже, до функції передається не сам об’єкт, а посилання на цей об’єкт. Таким чином, у формальному параметрі дублюється значення посилання, а не значення самого об’єкта: створення нового об’єкта не відбувається. Якщо посилання на об’єкт R1 передається до функції, воно буде скопійовано у відповідний формальний параметр R2. Отже, посилання R2 та R1 вказують на один і той самий об’єкт. Якщо функція змінює об’єкт, на який вказує R2, вона, звісно, змінює й той, на який вказує R1, оскільки це один і той самий об’єкт.
![]() |
Це ілюструє наступний приклад:
' параметри
Option Explicit On
Option Strict On
' простори імен
Imports System
' тестова сторінка
Module test
Sub Main()
' особа p1
Dim p1 As New personne("Jean", "Dupont", 30)
' відображення p1
Console.Out.Write("Paramètre effectif avant modification : ")
p1.identifie()
' зміна p1
modifie(p1)
' перегляд p1
Console.Out.Write("Paramètre effectif après modification : ")
p1.identifie()
End Sub
Sub modifie(ByVal P As personne)
' перегляд особи P
Console.Out.Write("Paramètre formel avant modification : ")
P.identifie()
' редагування P
P.prenom = "Sylvie"
P.nom = "Vartan"
P.age = 52
' перегляд P
Console.Out.Write("Paramètre formel après modification : ")
P.identifie()
End Sub
End Module
Отримано такі результати:
Paramètre effectif avant modification : Jean,Dupont,30
Paramètre formel avant modification : Jean,Dupont,30
Paramètre formel après modification : Sylvie,Vartan,52
Paramètre effectif après modification : Sylvie,Vartan,52
Бачимо, що створено лише один об’єкт: об’єкт особи p1 з процедури Main, і що цей об’єкт дійсно було змінено функцією modifie.
3.1.16. Масив осіб
Об’єкт — це дані, як і будь-які інші, і тому кілька об’єктів можна об’єднати в масив:
' параметри
Option Explicit On
Option Strict On
' простори імен
Imports System
' тестова сторінка
Module test
Sub Main()
' таблиця осіб
Dim amis(2) As personne
amis(0) = New personne("Jean", "Dupont", 30)
amis(1) = New personne("Sylvie", "Vartan", 52)
amis(2) = New personne("Neil", "Armstrong", 66)
' відображення
Console.Out.WriteLine("----------------")
Dim i As Integer
For i = 0 To amis.Length - 1
amis(i).identifie()
Next i
End Sub
End Module
Інструкція Dim amis(2) As personne створює масив із 3 елементів типу personne. Ці 3 елементи ініціалізуються тут значеннями nothing, c.a.d, оскільки вони не посилаються на жодний об’єкт. Знову ж таки, через неточність формулювання ми говоримо про масив об’єктів, хоча насправді це лише масив посилань на об’єкти. Створення масиву об’єктів, який сам по собі є об’єктом, не створює жодного об’єкта типу його елементів: це потрібно зробити згодом. Отримуємо такі результати:
3.2. Спадкування на прикладі
3.2.1. Загальні відомості
Тут ми розглянемо поняття успадкування. Мета успадкування — «персоналізувати» існуючий клас, щоб він відповідав нашим потребам. Припустимо, що ми хочемо створити клас enseignant: вчитель — це особлива людина. Він має атрибути, яких не матиме інша людина: наприклад, предмет, який він викладає. Але він також має атрибути, властиві будь-якій людині: ім’я, прізвище та вік. Отже, вчитель повністю належить до класу personne, але має додаткові атрибути. Замість того, щоб створювати клас enseignant з нуля, краще скористатися набором властивостей класу personne, який ми адаптуємо до особливого характеру вчителів. Саме концепція успадкування дозволяє нам це зробити. Щоб показати, що клас enseignant успадковує властивості класу personne, ми напишемо:
Public Class enseignant
Inherits personne
Зверніть увагу на особливий синтаксис, що займає два рядки. Клас personne називається батьківським (або материнським) класом, а клас enseignant — похідним (або дочірнім) класом. Об’єкт enseignant має всі властивості об’єкта personne: він має ті самі атрибути та ті самі методи. Ці атрибути та методи батьківського класу не повторюються у визначенні дочірнього класу: достатньо вказати лише атрибути та методи, додані дочірнім класом. Припустимо, що клас personne визначено так:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
' клас «особа»
Public Class personne
' атрибути класу
Private Shared _nbPersonnes As Long = 0
' атрибути екземпляра
Private _prenom As [String]
Private _nom As [String]
Private _age As Integer
' конструктори
Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
' ще одна людина
_nbPersonnes += 1
' створення
Me._prenom = P
Me._nom = N
Me._age = age
' відстеження
Console.Out.WriteLine("Construction personne(string, string, int)")
End Sub
Public Sub New(ByVal P As personne)
' ще одна людина
_nbPersonnes += 1
' будівництво
Me._prenom = P._prenom
Me._nom = P._nom
Me._age = P._age
' супровід
Console.Out.WriteLine("Construction personne(string, string, int)")
End Sub
' властивість класу
Public Shared ReadOnly Property nbPersonnes() As Long
Get
Return _nbPersonnes
End Get
End Property
' властивості екземпляра
Public Property prenom() As String
Get
Return _prenom
End Get
Set(ByVal Value As String)
_prenom = Value
End Set
End Property
Public Property nom() As String
Get
Return _nom
End Get
Set(ByVal Value As String)
_nom = Value
End Set
End Property
Public Property age() As Integer
Get
Return _age
End Get
Set(ByVal Value As Integer)
' вік у межах норми?
If Value >= 0 Then
_age = Value
Else
Throw New Exception("âge (" & Value & ") invalide")
End If
End Set
End Property
Public ReadOnly Property identite() As String
Get
Return "personne(" & _prenom & "," & _nom & "," & age & ")"
End Get
End Property
End Class
Метод identifie було замінено на властивість identité, доступну лише для читання, яка ідентифікує особу. Створюємо клас enseignant, що успадковує клас personne:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
Public Class enseignant
Inherits personne
' атрибути
Private _section As Integer
' конструктор
Public Sub New(ByVal P As String, ByVal N As String, ByVal age As Integer, ByVal section As Integer)
MyBase.New(P, N, age)
Me._section = section
' відстеження
Console.Out.WriteLine("Construction enseignant(string,string,int,int)")
End Sub
' властивість розділу
Public Property section() As Integer
Get
Return _section
End Get
Set(ByVal Value As Integer)
_section = Value
End Set
End Property
End Class
Клас enseignant доповнює методи та атрибути класу personne:
- атрибут section, який є номером секції, до якої належить викладач у складі викладацького складу (загалом одна секція на дисципліну)
- новий конструктор, що дозволяє ініціалізувати всі атрибути викладача
Оголошення
Public Class enseignant
Inherits personne
вказує, що клас enseignant походить від класу personne.
3.2.2. Створення об’єкта «викладач»
Конструктор класу enseignant має такий вигляд:
' конструктор
Public Sub New(ByVal P As String, ByVal N As String, ByVal age As Integer, ByVal section As Integer)
MyBase.New(P, N, age)
Me._section = section
' відстеження
Console.Out.WriteLine("Construction enseignant(string,string,int,int)")
End Sub
Заява
Public Sub New(ByVal P As String, ByVal N As String, ByVal age As Integer, ByVal section As Integer)
зазначає, що конструктор отримує чотири параметри: P, N, age, section. Він повинен передати три з них — (P,N,age) — своєму базовому класу, в даному випадку класу personne. Відомо, що цей клас має конструктор person(string, string, int), який дозволить створити об’єкт типу «особа» з переданими параметрами (P,N,age). Клас [enseignant] передає параметри (P, N, age) своєму базовому класу наступним чином:
MyBase.New(P, N, age)
Після завершення створення об’єкта базового класу створення об’єкта enseignant продовжується виконанням тіла конструктора:
Me._section = section
Підсумовуючи, конструктор похідного класу:
- передає базовому класу параметри, необхідні для його створення
- використовує інші параметри для ініціалізації власних атрибутів
Можна було б написати так:
Public Sub New(ByVal P As String, ByVal N As String, ByVal age As Integer, ByVal section As Integer)
Me._prenom = P
Me._nom = N
Me._age = age
Me._section = section
' відстеження
Console.Out.WriteLine("Construction enseignant(string,string,int,int)")
End Sub
Це неможливо. У класі personne три поля — _prenom, _nom та _age — оголошено приватними (private). Прямий доступ до цих полів мають лише об’єкти того самого класу. Усі інші об’єкти, включаючи похідні об’єкти, як у цьому випадку, повинні використовувати публічні методи, щоб отримати до них доступ. Ситуація була б іншою, якби клас personne оголосив ці три поля захищеними (protected): тоді він дозволив би похідним класам мати прямий доступ до цих трьох полів. У нашому прикладі використання конструктора батьківського класу було, отже, правильним рішенням, і це є стандартним підходом: під час створення дочірнього об’єкта спочатку викликається конструктор батьківського об’єкта, а потім виконуються власні ініціалізації, що стосуються саме дочірнього об’єкта (section у нашому прикладі).
Скомпілюємо класи personne та enseignant у збірках:
dos>vbc /t:library personne.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>vbc /r:personne.dll /t:library enseignant.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
25/02/2004 10:08 1 828 personne.vb
25/02/2004 10:11 675 enseignant.vb
25/02/2004 10:12 223 test.vb
25/02/2004 10:16 4 096 personne.dll
25/02/2004 10:16 3 584 enseignant.dll
Слід зауважити, що для компіляції дочірнього класу enseignant довелося посилатися на файл personne.dll, який містить клас personne. Спробуємо написати першу тестову програму:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
Module test
Sub Main()
Console.Out.WriteLine(New enseignant("Jean", "Dupont", 30, 27).identite)
End Sub
End Module
Ця програма обмежується створенням об’єкта enseignant (new) та його ідентифікацією. Клас enseignant не має методу identité, але його батьківський клас має такий метод, який, до того ж, є публічним: завдяки успадкуванню він стає публічним методом класу enseignant. Отримано такі результати:
dos>vbc /r:personne.dll /r:enseignant.dll test.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>test
Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
personne(Jean,Dupont,30)
Бачимо, що:
- об’єкт personne було створено раніше, ніж об’єкт enseignant
- отриманий ідентифікатор належить об’єкту personne
3.2.3. Перевантаження методу або властивості
У попередньому прикладі ми отримали ідентифікатор частини personne, що відповідає викладачеві, але бракує певної інформації, властивої класу enseignant (секція). Тому доводиться написати властивість, що дозволяє ідентифікувати викладача:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
Public Class enseignant
Inherits personne
' атрибути
Private _section As Integer
' конструктор
Public Sub New(ByVal P As String, ByVal N As String, ByVal age As Integer, ByVal section As Integer)
MyBase.New(P, N, age)
Me._section = section
' відстеження
Console.Out.WriteLine("Construction enseignant(string,string,int,int)")
End Sub
' властивість розділу
Public Property section() As Integer
Get
Return _section
End Get
Set(ByVal Value As Integer)
_section = Value
End Set
End Property
' перевантаження властивості ідентичності
Public Shadows ReadOnly Property identite() As String
Get
Return "enseignant(" & MyBase.identite & "," & _section & ")"
End Get
End Property
End Class
Метод identite класу enseignant спирається на метод identite свого батьківського класу (MyBase.identite) для відображення своєї частини «personne», а потім доповнює її полем _section, яке є власним для класу enseignant. Звернімо увагу на оголошення властивості identite:
Public Shadows ReadOnly Property identite() As String
яка вказує, що властивість identite «приховує» однойменний метод, який міг би існувати в батьківському класі. Нехай маємо об’єкт enseignant E. Цей об’єкт містить у собі об’єкт personne:
![]() |
Властивість identity визначена як у класі enseignant, так і в його батьківському класі personne. У дочірньому класі enseignant перед властивістю identite має стояти ключове слово shadows, щоб вказати, що ми перевизначаємо нову властивість identite для класу enseignant.
Public Shadows ReadOnly Property identite() As String
Клас enseignant тепер має два властивості identite:
- одну, успадковану від батьківського класу personne
- власну
Якщо E є об’єктом класу enseignant, то E.identite позначає метод identite класу enseignant. Кажуть, що властивість identite батьківського класу «перевизначається» властивістю identite дочірнього класу. Загалом, якщо O є об’єктом, а M — методом, для виконання методу O.M система шукає метод M у такому порядку:
- у класі об’єкта O
- у його батьківському класі, якщо такий є
- у батьківському класі його батьківського класу, якщо такий існує
- і т. д.
Отже, успадкування дозволяє у дочірньому класі перевизначити методи/властивості з тими самими іменами, що й у батьківському класі. Саме це дає змогу адаптувати дочірній клас до власних потреб. У поєднанні з поліморфізмом, про який ми поговоримо трохи пізніше, перевантаження методів/властивостей є головною перевагою успадкування. Розглянемо той самий приклад, що й раніше:
' опції
Option Strict On
Option Explicit On
' простори імен
Imports System
Module test
Sub Main()
Console.Out.WriteLine(New enseignant("Jean", "Dupont", 30, 27).identite)
End Sub
End Module
Цього разу отримано такі результати:
Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
enseignant(personne(Jean,Dupont,30),27)
3.2.4. Поліморфізм
Розглянемо лінію класів: C0 C1 C2 … Cn, де Ci Cj, вказує, що клас Cj є похідним від класу Ci. Звідси випливає, що клас Cj має всі характеристики класу Ci, а також додаткові. Нехай Oi — об’єкти типу Ci. Допустимо записати:
Адже завдяки успадкуванню клас Cj має всі характеристики класу Ci, а також додаткові. Отже, об’єкт Oj типу Cj містить у собі об’єкт типу Ci. Операція
призводить до того, що Oi є посиланням на об’єкт типу Ci, що міститься в об’єкті Oj.
Те, що змінна Oi класу Ci може фактично посилатися не лише на об’єкт класу Ci, а й на будь-який об’єкт, похідний від класу Ci, називається поліморфізмом: здатність змінної посилатися на об’єкти різних типів. Розглянемо приклад і проаналізуємо таку функцію, незалежну від будь-якого класу:
Sub affiche(ByVal p As personne)
Можна також написати
або
В останньому випадку формальний параметр типу personne функції affiche отримає значення типу enseignant. Оскільки тип enseignant походить від типу personne, це допустимо.
3.2.5. Перевизначення та поліморфізм
Доповнимо нашу процедуру affiche:
Sub affiche(ByVal p As personne)
' відображення ідентифікатора p
Console.Out.WriteLine(p.identite)
End Sub
Метод p.identite повертає рядок, що ідентифікує об’єкт personne.. Що відбувається у випадку нашого попереднього прикладу для об’єкта enseignant:
Dim e As New enseignant("Lucile", "Dumas", 56, 61)
affiche(e)
Розглянемо наступний приклад:
' опції
Option Strict On
Option Explicit On
' простори імен
Imports System
Module test
Sub Main()
' викладач
Dim e As New enseignant("Lucile", "Dumas", 56, 61)
affiche(e)
' особа
Dim p As New personne("Jean", "Dupont", 30)
affiche(p)
End Sub
' відображає
Sub affiche(ByVal p As personne)
' афіша ідентичності p
Console.Out.WriteLine(p.identite)
End Sub
End Module
Отримано такі результати:
Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
personne(Lucile,Dumas,56)
Construction personne(string, string, int)
personne(Jean,Dupont,30)
Виконання показує, що інструкція p.identite щоразу виконувала властивість identite об’єкта personne, а саме об’єкт, що міститься в enseignant e, а потім саму personne p. Вона не адаптувалася до об’єкта, який фактично передавався як параметр до affiche. Було б краще мати повну ідентифікацію об’єкта enseignant e. Для цього позначення p.identite мало б посилатися на властивість identite об’єкта, на який насправді вказує p, а не на властивість identite частини «personne» об’єкта, на який насправді вказує p. Цей результат можна отримати, оголосивши identite як властивість, що підлягає перевизначенню (overridable), у базовому класі personne:
Public Overridable ReadOnly Property identite() As String
Get
Return "personne(" & _prenom & "," & _nom & "," & age & ")"
End Get
End Property
Ключове слово overridable робить identite властивістю, яку можна перевизначити, або віртуальною властивістю. Це ключове слово також може застосовуватися до методів. Дочірні класи, які перевизначають віртуальну властивість або метод, повинні використовувати ключове слово overrides замість shadows для позначення своєї перевизначеної властивості/методу. Так, у класі enseignant властивість identite визначено наступним чином:
' перевизначення властивості ідентичності
Public Overrides ReadOnly Property identite() As String
Get
Return "enseignant(" & MyBase.identite & "," & _section & ")"
End Get
End Property
Тестова програма:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
Module test
Sub Main()
' викладач
Dim e As New enseignant("Lucile", "Dumas", 56, 61)
affiche(e)
' особа
Dim p As New personne("Jean", "Dupont", 30)
affiche(p)
End Sub
' відображає
Sub affiche(ByVal p As personne)
' афіша, що ідентифікує п
Console.Out.WriteLine(p.identite)
End Sub
End Module
дає такі результати:
Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
enseignant(personne(Lucile,Dumas,56),61)
Construction personne(string, string, int)
personne(Jean,Dupont,30)
Цього разу ми отримали повну ідентифікацію викладача. Тепер переглянемо метод, а не властивість. Клас object є «батьківським» класом для всіх класів VB.NET. Отже, коли ми пишемо:
, ми неявним чином записуємо:
Клас object визначає віртуальний метод ToString:

Метод ToString повертає ім’я класу, до якого належить об’єкт, як показано в наступному прикладі:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
Module test2
Sub Main()
' викладач
Console.Out.WriteLine(New enseignant("Lucile", "Dumas", 56, 61).ToString())
' особа
Console.Out.WriteLine(New personne("Jean", "Dupont", 30).ToString())
End Sub
End Module
Отримано такі результати:
Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
enseignant
Construction personne(string, string, int)
personne
Слід зауважити, що хоча ми й не перевизначили метод ToString у класах personne та enseignant, проте можна побачити, що метод ToString класу object все ж здатний відображати справжню назву класу об’єкта. Перевизначимо метод ToString у класах personne та enseignant:
' ToString
Public Overrides Function ToString() As String
' присвоюємо властивість «ідентичність»
Return identite
End Function
Визначення є однаковим у обох класах. Розглянемо таку тестову програму:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
Module test
Sub Main()
' викладач
Dim e As New enseignant("Lucile", "Dumas", 56, 61)
affiche(e)
' особа
Dim p As New personne("Jean", "Dupont", 30)
affiche(p)
End Sub
' плакат
Sub affiche(ByVal p As personne)
' афіша, що ідентифікує п
Console.Out.WriteLine(p.identite)
End Sub
End Module
Результати виконання такі:
Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
enseignant(personne(Lucile,Dumas,56),61)
Construction personne(string, string, int)
personne(Jean,Dupont,30)
3.3. Визначення індексатора для класу
Розглянемо клас [ArrayList], який заздалегідь визначено на платформі .NET. Цей клас дозволяє зберігати об’єкти у списку. Він належить до простору імен [System.Collections]. У наведеному нижче прикладі ми використовуємо цей клас для зберігання списку осіб (у широкому розумінні):
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
Imports System.Collections
' клас listeDePersonnes
Public Class listeDePersonnes
Inherits ArrayList
' для додавання особи до списку
Public Overloads Sub Add(ByVal p As personne)
MyBase.Add(p)
End Sub
' індексатор
Default Public Shadows Property Item(ByVal i As Integer) As personne
Get
Return CType(MyBase.Item(i), personne)
End Get
Set(ByVal Value As personne)
MyBase.Item(i) = Value
End Set
End Property
' ще один індексатор
Default Public Shadows ReadOnly Property Item(ByVal N As String) As Integer
Get
' шукаємо особу на ім’я N
Dim i As Integer
For i = 0 To Count - 1
If CType(Me(i), personne).nom = N Then
Return i
End If
Next i
Return -1
End Get
End Property
' toString
Public Overrides Function ToString() As String
' повертає (елемент1, елемент2, ..., елементn)
Dim liste As String = "("
Dim i As Integer
' пробігаємо динамічний масив
For i = 0 To (Count - 2)
liste += "[" + Me(i).ToString + "]" + ","
Next i 'for
' останній елемент
If Count <> 0 Then
liste += "[" + Me(i).ToString + "]"
End If
liste += ")"
Return liste
End Function
End Class
Наведемо деяку інформацію про певні властивості та методи класу [ArrayList]:
атрибут, що вказує кількість елементів у списку | |
метод, що дозволяє додати об’єкт до списку | |
метод, що повертає i-й елемент списку |
Зауважимо, що для отримання елемента № i зі списку ми написали не [liste.Item(i)], а безпосередньо [liste(i)], що на перший погляд здається помилкою. Проте це можливо, оскільки клас [ArrayList] визначає властивість за замовчуванням [Item] за синтаксисом, аналогічним до такого:
Default Public Property Item(ByVal i As Integer) As Object
Get
...
End Get
Set(ByVal Value As personne)
...
End Set
End Property
Коли компілятор зустрічає запис [liste(i)], він перевіряє, чи клас [ArrayList] визначив властивість із таким сигнатурою:
Default Public Property Proc(ByVal var As Integer) As Type
У цьому випадку він знайде процедуру [Item]. Тоді він перетворить запис [liste(i)] на [liste.Item(i)]. Властивість [Item] називається індексованою за замовчуванням властивістю класу [ArrayList]. Виконання попередньої програми дає такі результати:
dos>vbc /r:personne.dll /r:enseignant.dll lstpersonnes1.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
11/03/2004 18:26 3 584 enseignant.dll
12/03/2004 15:34 3 584 lstpersonnes1.exe
12/03/2004 13:39 661 lstpersonnes1.vb
11/03/2004 18:26 4 096 personne.dll
dos>lstpersonnes1
Construction personne(string, string, int)
Construction personne(string, string, int)
Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
personne(paul,chenou,31)
personne(nicole,chenou,11)
enseignant(personne(jacques,sileau,33),61)
Створимо клас із назвою [listeDePersonnes], який буде списком осіб, тобто особливим списком, який, як видається, логічно вивести з класу [ArrayList]:
' опції
Option Strict On
Option Explicit On
' простори імен
Imports System
Imports System.Collections
' клас listeDePersonnes
Public Class listeDePersonnes
Inherits ArrayList
' щоб додати особу до списку
Public Shadows Sub Add(ByVal p As Object)
' p має бути особою
If Not TypeOf (p) Is personne Then
Throw New Exception("L'objet ajouté (" + p.ToString + ") n'est pas une personne")
Else
MyBase.Add(p)
End If
End Sub
' індексатор
Default Public Shadows Property Index(ByVal i As Integer) As personne
Get
Return CType(MyBase.Item(i), personne)
End Get
Set(ByVal Value As personne)
MyBase.Item(i) = Value
End Set
End Property
' toString
Public Overrides Function ToString() As String
' повертає (елемент1, елемент2, ..., елементn)
Dim liste As String = "("
Dim i As Integer
' проходимо по динамічному масиву
For i = 0 To (Count - 2)
liste += "[" + Me(i).ToString + "]" + ","
Next i 'for
' останній елемент
If Count <> 0 Then
liste += "[" + Me(i).ToString + "]"
End If
liste += ")"
Return liste
End Function
End Class
Цей клас має такі методи та властивості:
повертає рядок, що «представляє» вміст списку | |
метод, що дозволяє додати особу до списку | |
властивість, індексована за замовчуванням, що повертає особу i зі списку |
Розглянемо нові моменти:
Створено новий метод [Add].
' щоб додати особу до списку
Public Shadows Sub Add(ByVal p As Object)
' необхідно, щоб p був особою
If Not TypeOf (p) Is personne Then
Throw New Exception("L'objet ajouté (" + p.ToString + ") n'est pas une personne")
Else
MyBase.Add(p)
End If
End Sub
У батьківському класі [ArrayList] вже існує процедура з таким самим підписом, тому використовується ключове слово [Shadows], щоб вказати, що нова процедура замінює процедуру батьківського класу. Метод [Add] дочірнього класу перевіряє, чи доданий об’єкт дійсно є типом [personne] або похідним від нього за допомогою функції [TypeOf]. Якщо це не так, за допомогою інструкції [Throw] генерується виняток. Якщо доданий об’єкт дійсно має тип [personne], для додавання використовується метод [Add] базового класу.
Створюється індексована властивість:
' індексатор
Default Public Shadows Property Index(ByVal i As Integer) As personne
Get
Return CType(MyBase.Item(i), personne)
End Get
Set(ByVal Value As personne)
MyBase.Item(i) = Value
End Set
End Property
У базовому класі вже існує властивість за замовчуванням із назвою [Item] та такою самою сигнатурою. Тому доводиться використовувати ключове слово [Shadows], щоб вказати, що нова індексована властивість [Index] «приховає» властивість базового класу [Item]. Слід зауважити, що це справедливо навіть у тому випадку, якщо обидва властивості не мають однакової назви. Властивість [Index] дозволяє посилатися на особу № i зі списку. Вона спирається на властивість [Item] базового класу, щоб отримати доступ до елемента № i базового об’єкта [ArrayList]. Зміни типу вносяться з урахуванням того, що властивість [Item] працює з елементами типу [Object], тоді як властивість [Index] працює з елементами типу [personne].
Нарешті, ми перевизначаємо (Overrides) метод [ToString] класу [ArrayList]:
' toString
Public Overrides Function ToString() As String
' повертає (елемент1, елемент2, ..., елементn)
Dim liste As String = "("
Dim i As Integer
' проходимо по динамічному масиву
For i = 0 To (Count - 2)
liste += "[" + Me(i).ToString + "]" + ","
Next i 'for
' останній елемент
If Count <> 0 Then
liste += "[" + Me(i).ToString + "]"
End If
liste += ")"
Return liste
End Function
Цей метод повертає рядок у вигляді «(e1,e2,...,en)», де e1, e2, ..., en — елементи списку. Зверніть увагу на запис [Me(i)], який позначає елемент № i поточного об’єкта [Me]. Використовується індексована властивість за замовчуванням. Таким чином, [Me(i)] еквівалентно [Me.Index(i)].
Код класу розміщується у файлі [lstpersonnes2.vb] і компілюється:
dos>dir
11/03/2004 18:26 3 584 enseignant.dll
12/03/2004 15:45 970 lstpersonnes2.vb
11/03/2004 18:26 4 096 personne.dll
dos>vbc /r:personne.dll /r:enseignant.dll /t:library lstpersonnes2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
11/03/2004 18:26 3 584 enseignant.dll
12/03/2004 15:50 3 584 lstpersonnes2.dll
12/03/2004 15:45 970 lstpersonnes2.vb
11/03/2004 18:26 4 096 personne.dll
Створено тестову програму:
' опції
Option Explicit On
Option Strict On
' простори імен
Imports System
Imports System.Collections
Module test
Sub Main()
' створення порожнього списку осіб
Dim liste As listeDePersonnes = New listeDePersonnes
' створення осіб
Dim p1 As personne = New personne("paul", "chenou", 31)
Dim p2 As personne = New personne("nicole", "chenou", 11)
Dim e1 As enseignant = New enseignant("jacques", "sileau", 33, 61)
' заповнення списку
liste.Add(p1)
liste.Add(p2)
liste.Add(e1)
' перегляд списку
Console.Out.WriteLine(liste.ToString)
' додавання об’єкта, що не є особою
Try
liste.Add(4)
Catch e As Exception
Console.Error.WriteLine(e.Message)
End Try
End Sub
End Module
Вона компілюється:
dos>vbc /r:personne.dll /r:enseignant.dll /r:lstpersonnes2.dll test.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
11/03/2004 18:26 3 584 enseignant.dll
12/03/2004 15:50 3 584 lstpersonnes2.dll
12/03/2004 15:45 970 lstpersonnes2.vb
11/03/2004 18:26 4 096 personne.dll
12/03/2004 15:50 3 584 test.exe
12/03/2004 15:49 623 test.vb
потім виконано:
dos>test
Construction personne(string, string, int)
Construction personne(string, string, int)
Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
([personne(paul,chenou,31)],[personne(nicole,chenou,11)],[enseignant(personne(jacques,sileau,33),61)])
L'objet ajouté (4) n'est pas une personne
Можна було б написати
де l матиме вигляд [listeDePersonnes]. У цьому випадку список l потрібно індексувати не за номером елемента, а за іменем особи. Для цього визначаємо нову властивість, індексовану за замовчуванням:
' інший індексатор
Default Public Shadows ReadOnly Property Item(ByVal N As String) As Integer
Get
' пошук особи з ім'ям N
Dim i As Integer
For i = 0 To Count - 1
If CType(Me(i), personne).nom = N Then
Return i
End If
Next i
Return -1
End Get
End Property
Перший рядок
Default Public Shadows ReadOnly Property Index(ByVal N As String) As Integer
вказує, що ми знову створюємо властивість, індексовану за замовчуванням. Усі властивості за замовчуванням повинні мати однакову назву, у даному випадку [Index]. Нова властивість [Index] індексує клас listeDePersonnes за допомогою символьного рядка N. Результатом виклику listeDePersonnes(N) є ціле число. Це ціле число буде позицією у списку особи з ім’ям N або -1, якщо цієї особи немає у списку. Ми визначаємо лише властивість get, тим самим забороняючи запис listeDePersonnes ("nom")=valeur, який вимагав би визначення властивості set). Звідси й ключове слово [ReadOnly]. Ключове слово [Shadows] необхідне для приховування властивості за замовчуванням базового класу (хоча вона й не має такого самого сигнатура).
У тілі функції get виконується перебір списку осіб у пошуках імені N, переданого як параметр. Якщо його знайдено на позиції i, повертається i, інакше — -1.
Нова тестова програма може виглядати так:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
' тестова сторінка
Module test
Sub Main()
' список осіб
Dim l As New listeDePersonnes
' додавання осіб
l.Add(New personne("jean", "dumornet", 10))
l.Add(New personne("pauline", "duchemin", 12))
' перегляд
Console.Out.WriteLine(("l=" + l.ToString))
l.Add(New personne("jacques", "tartifume", 27))
Console.Out.WriteLine(("l=" + l.ToString))
' зміна елемента 1
l(1) = New personne("sylvie", "cachan", 5)
' перегляд елемента 1
Console.Out.WriteLine(("l[1]=" + l(1).ToString))
' перегляд списку l
Console.Out.WriteLine(("l=" + l.ToString))
' пошук осіб
Dim noms() As String = New [String]() {"cachan", "inconnu"}
Dim i As Integer
For i = 0 To noms.Length - 1
Dim inom As Integer = l(noms(i))
If inom <> -1 Then
Console.Out.WriteLine(("personne(" & noms(i) & ")=" & l(inom).ToString))
Else
Console.Out.WriteLine(("personne(" + noms(i) + ") n'existe pas"))
End If
Next i
End Sub
End Module
Виконання дає такі результати:
personne(string, string, int)
Construction personne(string, string, int)
l=([personne(jean,dumornet,10)],[personne(pauline,duchemin,12)])
Construction personne(string, string, int)
l=([personne(jean,dumornet,10)],[personne(pauline,duchemin,12)],[personne(jacques,tartifume,27)])
Construction personne(string, string, int)
l[1]=personne(sylvie,cachan,5)
l=([personne(jean,dumornet,10)],[personne(sylvie,cachan,5)],[personne(jacques,tartifume,27)])
personne(cachan)=personne(sylvie,cachan,5)
personne(inconnu) n'existe pas
3.4. Структури
Структура VB.NET безпосередньо походить від структури мови C і дуже нагадує клас. Структура визначається наступним чином:
Structure spersonne
' атрибути
...
' властивості
...
' конструктори
...
' методи
End Structure
Незважаючи на схожість у синтаксисі, між класом і структурою існують суттєві відмінності. Наприклад, поняття успадкування не існує для структур. Якщо ми створюємо клас, який не повинен бути похідним, то які відмінності між структурою та класом допоможуть нам зробити вибір між ними? Давайте розглянемо наступний приклад, щоб це з’ясувати:
' опції
Option Strict On
Option Explicit On
' простори імен
Imports System
' структура spersonne
Structure spersonne
Public nom As String
Public age As Integer
End Structure
' клас «cpersonne»
Class cpersonne
Public nom As String
Public age As Integer
End Class
' тестовий модуль
Public Module test
Sub Main()
' особа p1
Dim sp1 As spersonne
sp1.nom = "paul"
sp1.age = 10
Console.Out.WriteLine(("sp1=spersonne(" & sp1.nom & "," & sp1.age & ")"))
' особа p2
Dim sp2 As spersonne = sp1
Console.Out.WriteLine(("sp2=spersonne(" & sp2.nom & "," & sp2.age & ")"))
' sp2 змінено
sp2.nom = "nicole"
sp2.age = 30
' перевірка sp1 та sp2
Console.Out.WriteLine(("sp1=cpersonne(" & sp1.nom & "," & sp1.age & ")"))
Console.Out.WriteLine(("sp2=cpersonne(" & sp2.nom & "," & sp2.age & ")"))
' особа cp1
Dim cp1 As New cpersonne
cp1.nom = "paul"
cp1.age = 10
Console.Out.WriteLine(("cP1=cpersonne(" & cp1.nom & "," & cp1.age & ")"))
' особа c P2
Dim cp2 As cpersonne = cp1
Console.Out.WriteLine(("cP2=cpersonne(" & cp2.nom & "," & cp2.age & ")"))
' P2 змінено
cp2.nom = "nicole"
cp2.age = 30
' перевірка P1 та P2
Console.Out.WriteLine(("cP1=cpersonne(" & cp1.nom & "," & cp1.age & ")"))
Console.Out.WriteLine(("cP2=cpersonne(" & cp2.nom & "," & cp2.age & ")"))
End Sub
End Module
Якщо запустити цю програму, отримаємо такі результати:
sp1=spersonne(paul,10)
sp2=spersonne(paul,10)
sp1=cpersonne(paul,10)
sp2=cpersonne(nicole,30)
cP1=cpersonne(paul,10)
cP2=cpersonne(paul,10)
cP1=cpersonne(nicole,30)
cP2=cpersonne(nicole,30)
Там, де на попередніх сторінках цього розділу використовувався клас personne, тепер ми використовуємо структуру spersonne:
' структура особи
Structure spersonne
Public nom As String
Public age As Integer
End Structure
Заява
Dim sp1 As spersonne
створює структуру (ім'я, вік), а значенням sp1 є сама ця структура.
Оголошення
Dim cp1 As New cpersonne
створює об’єкт [cpersonne] (грубо кажучи, еквівалент нашої структури), а cp1 є адресою (посиланням) на цей об’єкт.
Підсумуємо
- у випадку структури значенням sp1 є сама структура
- у випадку класу значенням p1 є адреса створеного об’єкта
![]() |
![]() |
Коли в програмі записується
Dim sp2 As spersonne = sp1
створюється нова структура (ім’я, вік), яка ініціалізується значенням p1, тобто самою цією структурою.
![]() |
Отже, структура sp1 дублюється в sp2. Це копіювання значень.
Інструкція
Dim cp2 As cpersonne = cp1
діє інакше. Значення cp1 копіюється в cp2, але оскільки це значення насправді є адресою об’єкта, сам об’єкт не дублюється. Просто на нього є два посилання:
![]() |
У випадку структури, якщо змінити значення sp2, значення sp1 не змінюється, що й демонструє програма. У випадку об’єкта, якщо змінити об’єкт, на який вказує cp2, то об’єкт, на який вказує cp1, також змінюється, оскільки це один і той самий об’єкт. Це також підтверджують результати роботи програми.
Отже, з цих пояснень слід винести наступне:
- значенням змінної типу «структура» є сама структура
- значенням змінної типу «об’єкт» є адреса об’єкта, на який вказує змінна
Зрозумівши цю фундаментальну різницю, можна побачити, що структура дуже нагадує клас, як показано в наступному прикладі:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
' структура особи
Structure personne
' атрибути
Private _nom As String
Private _age As Integer
' властивості
Public Property nom() As String
Get
Return _nom
End Get
Set(ByVal Value As String)
_nom = value
End Set
End Property
Public Property age() As Integer
Get
Return _age
End Get
Set(ByVal Value As Integer)
_age = value
End Set
End Property
' Конструктор
Public Sub New(ByVal NOM As String, ByVal AGE As Integer)
_nom = NOM
_age = AGE
End Sub 'New
' TOSTRING
Public Overrides Function ToString() As String
Return "personne(" & nom & "," & age & ")"
End Function
End Structure
' тестовий модуль
Module test
Sub Main()
' особа p1
Dim p1 As New personne("paul", 10)
Console.Out.WriteLine(("p1=" & p1.ToString))
' особа p2
Dim p2 As personne = p1
Console.Out.WriteLine(("p2=" & p2.ToString))
' p2 змінено
p2.nom = "nicole"
p2.age = 30
' перевірка p1 та p2
Console.Out.WriteLine(("p1=" & p1.ToString))
Console.Out.WriteLine(("p2=" & p2.ToString))
End Sub
End Module
Отримуємо такі результати виконання:
Єдина помітна відмінність між структурою та класом полягає в тому, що у випадку класу об’єкти p1 та p2 мали б однакове значення наприкінці програми — саме значення p2.
3.5. Інтерфейси
Інтерфейс — це набір прототипів методів або властивостей, що утворюють контракт. Клас, який вирішує реалізувати інтерфейс, зобов’язується надати реалізацію всіх методів, визначених в інтерфейсі. Перевірку цієї реалізації здійснює компілятор. Ось, наприклад, визначення інтерфейсу Istats:
Будь-який клас, що реалізує цей інтерфейс, буде оголошений як
public class C
Implements Istats
...
function moyenne() as Double Implements Istats.moyenne
...
end function
function écartType () as Double Implements Istats. écartType
...
end function
end class
Методи [moyenne] та [écartType] повинні бути визначені у класі C. Розглянемо такий код:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
' структура
Public Structure élève
Public _nom As String
Public _note As Double
' конструктор
Public Sub New(ByVal NOM As String, ByVal NOTE As Double)
Me._nom = NOM
Me._note = NOTE
End Sub
End Structure
' клас «нотатки»
Public Class notes
' атрибут
Protected _matière As String
Protected _élèves() As élève
' виробник
Public Sub New(ByVal MATIERE As String, ByVal ELEVES() As élève)
' збереження даних про учнів та предмети
Me._matière = MATIERE
Me._élèves = ELEVES
End Sub
' ToString
Public Overrides Function ToString() As String
Dim valeur As String = "matière=" + _matière + ", notes=("
Dim i As Integer
' об'єднуємо всі оцінки
For i = 0 To (_élèves.Length - 1) - 1
valeur &= "[" & _élèves(i)._nom & "," & _élèves(i)._note & "],"
Next i
'остання оцінка
If _élèves.Length <> 0 Then
valeur &= "[" & _élèves(i)._nom & "," & _élèves(i)._note & "]"
End If
valeur += ")"
' кінець
Return valeur
End Function
End Class
Клас notes об'єднує оцінки класу з певного предмета:
Public Class notes
' атрибут
Protected _matière As String
Protected _élèves() As élève
Атрибути оголошуються як protected, щоб бути доступними для похідного класу. Тип élève — це структура, що зберігає ім’я учня та його оцінку з предмета:
Public Structure élève
Public _nom As String
Public _note As Double
' конструктор
Public Sub New(ByVal NOM As String, ByVal NOTE As Double)
Me._nom = NOM
Me._note = NOTE
End Sub
End Structure
Ми вирішили вивести цей клас notes у клас notesStats, який матиме два додаткові атрибути — середнє значення та стандартне відхилення оцінок:
Public Class notesStats
Inherits notes
Implements Istats
' атрибути
Private _moyenne As Double
Private _écartType As Double
Клас notesStats реалізує наступний інтерфейс Istats:
Це означає, що клас notesStats повинен мати два методи з іменами moyenne та écartType із сигнатурою, вказаною в інтерфейсі Istats. Клас notesStats має такий вигляд:
' опції
Option Strict On
Option Explicit On
' простори імен
Imports System
Public Class notesStats
Inherits notes
Implements Istats
' атрибути
Private _moyenne As Double
Private _écartType As Double
' конструктор
Public Sub New(ByVal MATIERE As String, ByVal ELEVES() As élève)
MyBase.New(MATIERE, ELEVES)
' обчислення середнього значення оцінок
Dim somme As Double = 0
Dim i As Integer
For i = 0 To ELEVES.Length - 1
somme += ELEVES(i)._note
Next i
If ELEVES.Length <> 0 Then
_moyenne = somme / ELEVES.Length
Else
_moyenne = -1
End If
' стандартне відхилення
Dim carrés As Double = 0
For i = 0 To ELEVES.Length - 1
carrés += Math.Pow(ELEVES(i)._note - _moyenne, 2)
Next i
If ELEVES.Length <> 0 Then
_écartType = Math.Sqrt((carrés / ELEVES.Length))
Else
_écartType = -1
End If
End Sub
' ToString
Public Overrides Function ToString() As String
Return MyBase.ToString() & ",moyenne=" & _moyenne & ",écart-type=" & _écartType
End Function 'ToString
' методи інтерфейсу Istats
Public Function moyenne() As Double Implements Istats.moyenne
' повертає середнє значення оцінок
Return _moyenne
End Function
Public Function écartType() As Double Implements Istats.écartType
' повертає стандартне відхилення
Return _écartType
End Function
End Class
Середнє значення _moyenne та стандартне відхилення _ecartType обчислюються під час створення об’єкта. Тому методи moyenne та écartType повинні лише повертати значення атрибутів _moyenne та _ecartType. Обидва методи повертають -1, якщо масив учнів порожній.
Наступний тестовий клас:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
Module test
Sub Main()
' деякі учні та оцінки
Dim ELEVES() As élève = {New élève("paul", 14), New élève("nicole", 16), New élève("jacques", 18)}
' які зберігаються в об’єкті «оцінки»
Dim anglais As New notes("anglais", ELEVES)
' і які відображаються
Console.Out.WriteLine((anglais.ToString))
' те саме з середнім значенням та стандартним відхиленням
anglais = New notesStats("anglais", ELEVES)
Console.Out.WriteLine((anglais.ToString))
End Sub
End Module
дає такі результати:
matière=anglais, notes=([paul,14],[nicole,16],[jacques,18])
matière=anglais, notes=([paul,14],[nicole,16],[jacques,18]),moyenne=16,écart-type=1,63299316185545
Клас notesStats цілком міг би реалізувати методи moyenne та écartType для себе, не вказуючи, що він реалізує інтерфейс Istats. У чому полягає користь інтерфейсів? Справа в тому, що функція може приймати як формальний параметр дані типу інтерфейсу I. Тоді будь-який об’єкт класу C, що реалізує інтерфейс I, може бути фактичним параметром цієї функції. Розглянемо такий приклад:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
' інтерфейс Iexample
Public Interface Iexemple
Function ajouter(ByVal i As Integer, ByVal j As Integer) As Integer
Function soustraire(ByVal i As Integer, ByVal j As Integer) As Integer
End Interface
' перший клас
Public Class classe1
Implements Iexemple
Public Function ajouter(ByVal a As Integer, ByVal b As Integer) As Integer Implements Iexemple.ajouter
Return a + b + 10
End Function
Public Function soustraire(ByVal a As Integer, ByVal b As Integer) As Integer Implements Iexemple.soustraire
Return a - b + 20
End Function
End Class
'другий клас
Public Class classe2
Implements Iexemple
Public Function ajouter(ByVal a As Integer, ByVal b As Integer) As Integer Implements Iexemple.ajouter
Return a + b + 100
End Function
Public Function soustraire(ByVal a As Integer, ByVal b As Integer) As Integer Implements Iexemple.soustraire
Return a - b + 200
End Function
End Class
Інтерфейс Iexemple визначає два методи: ajouter та soustraire. Класи classe1 та classe2 реалізують цей інтерфейс. Зауважимо, що ці класи нічого іншого не роблять — це зроблено для спрощення прикладу. Тепер розглянемо наступний приклад:
' опції
Option Strict On
Option Explicit On
' простори імен
Imports System
' тестовий клас
Module test
'обчислити
Sub calculer(ByVal i As Integer, ByVal j As Integer, ByVal inter As Iexemple)
Console.Out.WriteLine(inter.ajouter(i, j))
Console.Out.WriteLine(inter.soustraire(i, j))
End Sub
' функція Main
Sub Main()
' створення двох об’єктів «клас1» та «клас2»
Dim c1 As New classe1
Dim c2 As New classe2
' виклики статичної функції «calculer»
calculer(4, 3, c1)
calculer(14, 13, c2)
End Sub
End Module
Функція calculer приймає як параметр елемент типу Iexemple. Отже, вона може отримувати для цього параметра як об’єкт типу classe1, так і об’єкт типу classe2. Саме це робиться у процедурі Main з такими результатами:
Отже, бачимо, що маємо тут властивість, близьку до поліморфізму, який ми розглядали для класів. Якщо набір класів Ci, що не пов'язані між собою спадковістю (тож не можна використовувати поліморфізм спадковості), має набір методів з однаковою сигнатурою, може бути цікаво об'єднати ці методи в інтерфейс I, який успадковуватимуть усі відповідні класи. Інстанції цих класів Ci можна тоді використовувати як аргументи функцій, що приймають аргумент типу I, c.a.d. Це функції, які використовують лише методи об’єктів Ci, визначені в інтерфейсі I, а не конкретні атрибути та методи окремих класів Ci. Нарешті, зауважимо, що успадкування інтерфейсів може бути множинним, c.a.d. Можна записати
Public Class classe
Implements I1,I2,...
де Ij — це інтерфейси.
3.6. Простори імен
Щоб вивести рядок на екран, ми використовуємо оператор
Якщо розглянути визначення класу Console
Namespace: System
Assembly: Mscorlib (in Mscorlib.dll)
, то побачимо, що він належить до простору імен System. Це означає, що клас Console слід позначати як System.Console, і насправді слід писати:
Цього можна уникнути, використовуючи клаузулу imports:
Кажуть, що простір імен System імпортується за допомогою клаузули imports. Коли компілятор натрапить на ім’я класу (у даному випадку Console), він шукатиме його в різних просторах імен, імпортованих за допомогою операторів imports. Тут він знайде клас Console у просторі імен System. Тепер звернімо увагу на другу інформацію, пов’язану з класом Console:
Assembly: Mscorlib (in Mscorlib.dll)
Цей рядок вказує, у якому «збірці» знаходиться визначення класу Console. Ця інформація може виявитися корисною, коли ви компілюєте поза Visual Studio.NET і вам потрібно вказати посилання на різні dll, що містять класи, які ви маєте використовувати. Нагадаємо, що для посилання на dll, необхідні для компіляції класу, пишуть:
Створюючи клас, його можна розмістити в просторі імен. Мета цих просторів імен — уникнути конфліктів імен між класами, наприклад, під час їх продажу. Розглянемо дві компанії E1 та E2, які розповсюджують класи, упаковані відповідно у dll, E1.dll та E2.dll. Нехай клієнт C купує ці два набори класів, у яких обидві компанії визначили клас personne. Клієнт C компілює програму наступним чином:
Якщо у вихідному коді prog.vb використовується клас personne, компілятор не зможе визначити, чи слід йому взяти клас personne із E1.dll чи з E2.dll. Він повідомить про помилку. Якщо компанія E1 подбає про те, щоб створювати свої класи в просторі імен під назвою E1, а компанія E2 — у просторі імен під назвою E2, то обидва класи personne називатимуться E1.personne та E2.personne. Клієнт повинен використовувати у своїх класах або E1.personne, або E2.personne, але не personne. Простір імен дозволяє усунути неоднозначність. Щоб створити клас у просторі імен, потрібно написати:
Namespace istia.st
Public Class personne
' визначення класу
...
end Class
end Namespace
Для прикладу створимо в просторі імен наш клас personne, який ми розглядали раніше. Виберемо istia.st як простір імен. Клас personne стає таким:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
' створення простору імен istia.st
Namespace istia.st
Public Class personne
' атрибути
Private prenom As String
Private nom As String
Private age As Integer
' метод
Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
Me.prenom = P
Me.nom = N
Me.age = age
End Sub
' метод
Public Sub identifie()
Console.Out.WriteLine((prenom & "," & nom & "," & age))
End Sub
End Class
End Namespace
Цей клас компілюється в personne.dll:
dos>vbc /t:library personne.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
Тепер використаємо клас personne у тестовому класі:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
Imports istia.st
' тестова сторінка
Public Module test
Sub Main()
Dim p1 As New personne
p1.initialise("Jean", "Dupont", 30)
p1.identifie()
End Sub
End Module
Щоб уникнути написання
Dim p1 As New istia.st.personne
ми імпортували простір імен istia.st за допомогою оператора imports:
Imports istia.st
Тепер скомпілюємо тестову програму:
dos>dir
12/03/2004 18:06 3 584 personne.dll
11/03/2004 18:27 610 personne.vb
12/03/2004 18:05 254 test.vb
dos>vbc /r:personne.dll test.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
12/03/2004 18:06 3 584 personne.dll
11/03/2004 18:27 610 personne.vb
12/03/2004 18:08 3 072 test.exe
12/03/2004 18:05 254 test.vb
У результаті створюється файл test.exe, виконання якого дає такі результати:
3.7. Приклад IMPOTS
Повернемося до розрахунку податку, який вже розглядався в попередньому розділі, і обробимо його за допомогою класу. Нагадаємо задачу:
Розглянемо спрощений випадок, коли платник податків має задекларувати лише свою заробітну плату:
- розраховуємо кількість часток працівника nbParts=nbEnfants/2 +1, якщо він не одружений, nbEnfants/2+2, якщо він одружений, де nbEnfants — кількість його дітей.
- якщо у нього щонайменше троє дітей, він отримує на пів частки більше
- розраховується його оподатковуваний дохід R = 0,72*S, де S — його річна заробітна плата
- розраховується його сімейний коефіцієнт QF = R / nbParts
- розраховується його податок I. Розглянемо таку таблицю:
12620,0 | 0 | 0 |
13190 | 0,05 | 631 |
15640 | 0,1 | 1290,5 |
24740 | 0,15 | 2072,5 |
31810 | 0,2 | 3309,5 |
39970 | 0,25 | 4900 |
48360 | 0,3 | 6898,5 |
55790 | 0,35 | 9316,5 |
92970 | 0,4 | 12106 |
127860 | 0,45 | 16754,5 |
151250 | 0,50 | 23147,5 |
172040 | 0,55 | 30710 |
195 000 | 0,60 | 39312 |
0 | 0,65 | 49062 |
Кожен рядок має 3 поля. Щоб обчислити податок I, шукаємо перший рядок, у якому QF <= поле1. Наприклад, якщо QF = 23000, то знайдемо рядок
Тоді податок I дорівнює 0,15*R - 2072,5*nbParts. Якщо QF є таким, що співвідношення QF<=поле1 ніколи не виконується, то використовуються коефіцієнти останнього рядка. У даному випадку:
що дає податок I=0,65*R - 49062*nbParts.
Клас «impot» буде визначено наступним чином:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
Public Class impot
' дані, необхідні для розрахунку податку
' походять із зовнішнього джерела
Private limites(), coeffR(), coeffN() As Decimal
' виробник
Public Sub New(ByVal LIMITES() As Decimal, ByVal COEFFR() As Decimal, ByVal COEFFN() As Decimal)
' перевіряється, чи всі 3 масиви мають однаковий розмір
Dim OK As Boolean = LIMITES.Length = COEFFR.Length And LIMITES.Length = COEFFN.Length
If Not OK Then
Throw New Exception("Les 3 tableaux fournis n'ont pas la même taille(" & LIMITES.Length & "," & COEFFR.Length & "," & COEFFN.Length & ")")
End If
' все гаразд
Me.limites = LIMITES
Me.coeffR = COEFFR
Me.coeffN = COEFFN
End Sub
' розрахунок податку
Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Long) As Long
' розрахунок кількості часток
Dim nbParts As Decimal
If marié Then
nbParts = CDec(nbEnfants) / 2 + 2
Else
nbParts = CDec(nbEnfants) / 2 + 1
End If
If nbEnfants >= 3 Then
nbParts += 0.5D
End If
' розрахунок оподатковуваного доходу та сімейного коефіцієнта
Dim revenu As Decimal = 0.72D * salaire
Dim QF As Decimal = revenu / nbParts
' розрахунок податку
limites((limites.Length - 1)) = QF + 1
Dim i As Integer = 0
While QF > limites(i)
i += 1
End While
Return CLng(revenu * coeffR(i) - nbParts * coeffN(i))
End Function
End Class
Створюється об’єкт «податок» із даними, що дозволяють розрахувати податок платника податків. Це стабільна частина об’єкта. Після створення цього об’єкта можна неодноразово викликати його метод calculer, який обчислює податок платника податків на основі його сімейного стану (одружений чи ні), кількості дітей та річної заробітної плати. Тестова програма може виглядати так:
' параметри
Option Strict On
Option Explicit On
' простори імен
Imports System
Imports Microsoft.VisualBasic
Module test
Sub Main()
' інтерактивна програма розрахунку податку
' користувач вводить три значення з клавіатури: одружений nbEnfants заробітна плата
' після чого програма відображає суму податку до сплати
Const syntaxe As String = "syntaxe : marié nbEnfants salaire" + ControlChars.Lf + "marié : o pour marié, n pour non marié" + ControlChars.Lf + "nbEnfants : nombre d'enfants" + ControlChars.Lf + "salaire : salaire annuel en F"
' таблиці даних, необхідні для розрахунку податку
Dim limites() As Decimal = {12620D, 13190D, 15640D, 24740D, 31810D, 39970D, 48360D, 55790D, 92970D, 127860D, 151250D, 172040D, 195000D, 0D}
Dim coeffR() As Decimal = {0D, 0.05D, 0.1D, 0.15D, 0.2D, 0.25D, 0.3D, 0.35D, 0.4D, 0.45D, 0.5D, 0.55D, 0.6D, 0.65D}
Dim coeffN() As Decimal = {0D, 631D, 1290.5D, 2072.5D, 3309.5D, 4900D, 6898.5D, 9316.5D, 12106D, 16754.5D, 23147.5D, 30710D, 39312D, 49062D}
' створення об’єкта «податок»
Dim objImpôt As impot = Nothing
Try
objImpôt = New impot(limites, coeffR, coeffN)
Catch ex As Exception
Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
Environment.Exit(1)
End Try
' нескінченний цикл
Dim marié As String
Dim nbEnfants As Integer
Dim salaire As Long
While True
' запитуються параметри розрахунку податку
Console.Out.Write("Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :")
Dim paramètres As String = Console.In.ReadLine().Trim()
' що робити?
If paramètres Is Nothing OrElse paramètres = "" Then
Exit While
End If
' перевірка кількості аргументів у введеному рядку
Dim erreur As Boolean = False
Dim args As String() = paramètres.Split(Nothing)
Dim nbParamètres As Integer = args.Length
If nbParamètres <> 3 Then
Console.Error.WriteLine(syntaxe)
erreur = True
End If
' перевірка правильності параметрів
If Not erreur Then
' одружений
marié = args(0).ToLower()
If marié <> "o" And marié <> "n" Then
erreur = True
End If
' nbEnfants
Try
nbEnfants = Integer.Parse(args(1))
If nbEnfants < 0 Then
Throw New Exception
End If
Catch
erreur = True
End Try
' зарплата
Try
salaire = Integer.Parse(args(2))
If salaire < 0 Then
Throw New Exception
End If
Catch
erreur = True
End Try
End If
' якщо параметри правильні — розраховується податок
If Not erreur Then
Console.Out.WriteLine(("impôt=" & objImpôt.calculer(marié = "o", nbEnfants, salaire) & " F"))
Else
Console.Error.WriteLine(syntaxe)
End If
End While
End Sub
End Module
Ось приклад виконання попередньої програми:
dos>vbc /t:library impots.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dir>dir
12/03/2004 18:24 4 096 impots.dll
12/03/2004 18:20 1 483 impots.vb
12/03/2004 18:21 2 805 test.vb
dos>vbc /r:impots.dll test.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
12/03/2004 18:24 4 096 impots.dll
12/03/2004 18:20 1 483 impots.vb
12/03/2004 18:26 6 144 test.exe
12/03/2004 18:21 2 805 test.vb
dos>test
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :x x x
syntaxe : marié nbEnfants salaire
marié : o pour marié, n pour non marié
nbEnfants : nombre d'enfants
salaire : salaire annuel en F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 2 200000
impôt=22504 F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :





