:: Меню ::

Головна
  • Про сайт
  • Введення
  • Середовище програмування VB .NET: Visual Studio .NET
  • Вирази, оператори і передача управління
  • Класи і об'єкти
  •  Спадкоємство і інтерфейси
  • Обробка подій і делегати
  • Обробка помилок в VB .NET
  • Форми Windows, графічний вивід і друк
  • Уведення-виведення
  •  Багатопотокові застосування
  • Підтримка баз-даних в VB .NET
  • Короткий огляд ASP .NET
  • Складки .NET, установка додатків і COM Interop
  • Книга для гостей
    Контакти
    Добавити у вибране

    :: Друзі ::

     
     

    :: Лічильники ::

    = =

     

     

     

     

    Абстрактні базові класи

    На стадії проектування спадкових зв'язків в програмі часто з'ясовується, що багато класів володіють цілим рядом схожих рис. Наприклад, позаштатні співробітники не відносяться до постійних працівників, але і ті та інші володіють рядом загальних атрибутів — ім'ям, адресою, кодом платника податків і так далі Було б логічне виділити всі загальні атрибути в базовий клас Payabl eent i ty. Цей прийом, званий факторингом, часто використовується при проектуванні класів і дозволяє довести абстракцію до її логічного завершення.

    У класах, отриманих в результаті факторингу, деякі методи і властивості неможливо реалізувати, оскільки вони є загальними для всіх класів в ієрархії спадкоємства. Наприклад, клас Payabl eent i ty, від якого створюються похідні класи штатних і позаштатних працівників, може містити властивість з ім'ям TAXID. Зазвичай в процедурі цієї властивості слід було б організувати перевірку коди платника податків, але для деяких категорій позаштатних працівників ці коди мають особливий формат. Отже, перевірка цієї властивості має бути реалізована не в базовому класі Payabl eentity, а в похідних класах, оскільки лише вони знають, як повинен виглядати правильний код.

    У таких ситуаціях зазвичай визначається абстрактний базовий клас. Абстрактним називається клас, що містить хоч би одну функцію з ключовим словом Mustoverride; при цьому сам клас позначається ключовим словом Mustlnherit. Нижче показано, як може виглядати абстрактний клас Payabl eentity:

    Public Mustlnherit Class Payableentity

    Private m_name As String

    Public Sub New(Byval itsname As String)

    m_name = itsname

    End Sub

    Readonly Property Thename()As String

    Get

    Return m_name

    End Get

    End Property

    Public Mustoverride Property TAXID()As String

    End Class

    Звернете увагу: властивість TAXID, помічена ключовим словом Mustoverride, тільки оголошується без фактичної реалізації. Члени класів, помічені ключовим словом Mustoverride, складаються з одних заголовків і не містять команд End Property, End Sub і End Function. Доступне тільки для читання властивість Thename при цьому реалізована; з цього виходить, що абстрактні класи можуть містити як абстрактні, так і реалізовані члени. Нижче приведений приклад класу Єгор! оуєе, похідного від абстрактного класу Payableentity (ключові рядки виділені жирним шрифтом):

    Public Class Employee

    Inherits Payableentity

    Private m_salary As Decimal

    Private m_taxid As String

    Private Const LIMIT As Decimal = 0.1d

    Public Sub Newcbyval thename As String, Byval cursalary As Decimal.

    Byval TAXID As String) Mybase.New(thename)

    m_salary = cursalary

    m_taxid = TAXID End Sub

    Public Overrides Property TAXID() As String Get

    Return m_taxid

    End Get

    Set(Byval Value As String)

    If Value.Length <> 11 then

    ' Див. розділ 7 Else

    m_taxid = Value

    End If

    End Set

    End Property

    Readonly Property Salary() As Decimal Get

    Return Myclass.m_Salary

    End Get

    End Property

    Public Overridable Overloads Sub Raisesalary(Byval Percent As Decimal)

    If Percent > LIMIT Then

    ' Операція заборонена - необхідний пароль

    Console.WriteLineC'NEED PASSWORD TO RAISE SALARY MORE " & _

    "THAN LIMIT!!!!") Else

    m_salary =(1d + Percent) * m_salary

    End If

    End Sub

    Public Overridable Overloads Sub Raisesalary(Byval Percent As

    Decimal. Byval Password As String) If Password ="special" Then

    m_salary MID + Percent) * m_salary

    End If

    End Sub

    End Class

    Перший ключовий рядок розташований усередині конструктора, який тепер повинен викликати конструктор абстрактного базового класу для того, щоб правильно задати ім'я. У другому виділеному фрагменті визначається елементарна реалізація для властивості Taxld, оголошеної з ключовим словом Mustoverride (у приведеному прикладі нове значення властивості не перевіряється, як слід було б зробити в практичному прикладі).

    Нижче приведена процедура Sub Mai n, призначена для тестування цієї програми:

    Sub Main()

    Dim tom As New Employee("Tom". 50000. "111-11-1234")

    Dim sally As New Programmed "Sally", 150000. "111-11-2234".)

    Console.Wri teli ne(sa1ly.TheName)

    Dim ouremployees(l) As Employee

    ouremployees(0) = tom

    ouremployees(l) = sally

    Dim anemployee As Employee

    For Each anemployee In ouremployees anemployee.RaiseSalary(0.lD)

    Console.WriteLine(anemployee.TheName & "has tax id " & _

    anemployee.TaxID & ".salary now is " & anemployee.Salary())

    Next

    Consol e.ReadLine() End Sub

    У програмі неможливо створити екземпляр класу, оголошеного з ключовим словом Mustlnherit. Наприклад, при спробі виконання наступної команди:

    Dim Nogood As New Payableentity("can't do")

    компілятор виводить повідомлення про помилку:

    Class 'Payableentity' is not creatable because it contains at least one member marked as 'Mustoverride' that hasn't been overridden.

    Проте об'єкт похідного класу можна привласнити змінній або контейнеру абстрактного базового класу, що дає можливість використовувати в програмі поліморфні виклики:

    Dim torn As New Employee("Tom". 50000, "123-45-6789")

    Dim whotopay(13) As Payableentity whotopay(0) = tom

    Теоретично клас Mustlnherit може не містити жодного члена з ключовим сло-вом Mustoverride (хоча це виглядатиме декілька дивно).

    Приклад: клас Collectionbase

    При використанні класів колекцій .NET Framework (таких, як Arraylist і Hashtable) виникає несподівана проблема: ці класи призначені для зберігання узагальненого типу Object, тому прочитані з них об'єкти завжди приходітся перетворювати до початкового типу функцією Стуре. Також виникає небезпека того, що хто-небудь збереже в контейнері об'єкт іншого типу і спроба виклику Стуре завершиться невдачею. Проблема вирішується використанням колекцій з сильною типізацією — контейнерів, що дозволяють зберігати об'єкти конкретного типу і типів, похідних від нього.

    Хорошим прикладом абстрактного базового класу .NET Framework є клас Collectionbase. Класи, похідні від Coll ectionbase, використовуються для побудови колекцій з сильною типізацією (перш ніж створювати власні класи колекцій, похідні від Coll ectionbase, переконаєтеся в тому, що потрібні класи відсутні в просторі імен System.Collections.Specialized). Колекції, безпечні по відношенню до типів, будуються на основі абстрактного базового класу System. Collections. Collectionbase; від вас лише потрібно реалізувати методи Add і Remove, а також властивість Item. Зберігання даних у внутрішньому списку реалізоване на рівні класу System. Collections. Collectionbase, який і виконує решту всіх операцій.

    Розглянемо приклад створення спеціалізованих колекцій (передбачається, що проект містить клас Employee або посилання на нього):

    1 Public Class Employees

    2 Inherits System.Col lections.CollectionBase

    3 ' Метод Add включає в колекцію тільки об'єкти класу Employee.

    4 ' Виклик передоручається методу Add внутрішнього об'єкту List.

    5 Public Sub Addtbyval aemployee As Employee)

    6 List.Add(aemployee)

    7 End Sub

    8 Public Sub Remove(Byval index As Integer)

    9 If index > Count-1 Or index < 0 Then

    10 ' Індекс за межами інтервалу, ініціювати виключення (розділ 7)

    11 Msgbox("Can't add this item")' Msgbox умовно замінює виключення

    12 Else

    13 List.RemoveAt(index)

    14 End If

    15 End Sub

    16

    17 Default Public Readonly Property Item(Byval index As Integer) As Employee

    18 Get

    19 Return Ctype(List.Item(index). Employee)

    20 End Get

    21 End Property

    22 End Class

    У рядках 5-7 абстрактний метод Add базового класу реалізується передачею виклику внутрішньому об'єкту List; метод приймає для включення в колекцію тільки об'єкти Empl oyee. У рядках 8-10 реалізований метод Remove. Цього разу ми також використовуємо властивість Count внутрішнього об'єкту List, щоб переконатися в тому, що об'єкт, що видаляється, не знаходиться перед початком або після кінця списку. Нарешті, властивість Item реалізується в рядках 17-21. Воно оголошується властивістю за умовчанням, оскільки користувачі зазвичай чекають від колекцій саме такої поведінки. Властивість оголошується доступною тільки для читання, щоб додавання нових елементів в колекцію могло здійснюватися тільки методом Add. Звичайно, властивість можна було оголосити і доступним для читання/запису, але тоді було б потрібно додатковий код для перевірки індексу елемен, що додававсята. Наступний фрагмент перевіряє роботу спеціалізованої колекції; неприпустима операція включення нового елементу (у рядку, виділеному жирним шрифтом) закоментована:

    Sub Main()

    Dim torn As New Employee("Tom", 50000)

    Dim sally As New Employee("Sally", 60000)

    Dim myemployees As New Employees()

    myemployees.Add(tom)

    myemployees.Add(sally)

    ' myemployees.Add("Tom")

    Dim aemployee As Employee

    For Each aemployee In myemployees

    Console.WriteLine(aemployee.TheName)

    Next

    Console. Readline()

    End Sub

    Спробуйте прибрати коментар з рядка myempl oyees. Add("Tom"). Програма перестане компілюватися, і ви отримаєте наступне повідомлення про помилку:

    C:\book to comp \chapter 5\employeesclass\employeesclass\modulel.vb(9):

    A value of type 'String'cannot be converted to 'Employeesclass.Employee'.

    Перед вами чудовий приклад того, якими перевагами VB .NET володіє перед включенням в колишніх версіях VB. Звичайно, ми продовжуємо передоручати виклики внутрішньому об'єкту, щоб позбавитися від додаткової роботи, але можливість перебору елементів в циклі For-each з'являється автоматично, оскільки наш клас є похідним від класу з підтримкою For-each!

    Кореневий базовий клас Object

    Вся робота .NET Framework (а отже, і VB .NET) заснована на тому, що кожен тип є похідним від кореневого класу Object, загального предка всіх класів (у ООП такі класи іноді називаються космічними (space) базовими класами). До класу Object сходять всі типи, як посилальні (екземпляри класів), так і структурні (числові типи і дати, перераховувані типи і структури). Зокрема, з цього виходить, що будь-якій функції, одержуючій параметр типу Object, можна передати параметр довільного типу (оскільки головне правило спадкоємства, згадуване на початку розділу, вимагає, щоб змінна похідного типу могла використовуватися в будь-якому контексті замість змінної базового типу).
    Програмісти з досвідом роботи в ранніх версіях VB іноді представляють тип Object як аналог сумнозвісного типу Variant. He піддайтеся цій спокусі! Тип Variant був всього лише одним з типів даних, який дозволяв зберігати інші типи даних; тип Object є кореневим базовим класом, на якому завершується вся ієрархія спадкоємства в .NET.

    Клас Object містить ряд вбудованих логічних функцій, призначених для перевірки типу об'єктною змінною:

    • Isarray: функція перевіряє, чи містить об'єктна змінна масив.
    • Isdate: функція перевіряє, чи можна інтерпретувати об'єкт як дату і час.
    • Isnumeri з: функція перевіряє, чи можна інтерпретувати об'єкт як число.

    Нащадки класу Object діляться на дві категорії: структурні типи, похідні від System. Val uetype (базовий клас всіх структурних типів), і посилальні типи, похідні безпосередньо від Object. Щоб дізнатися, чи належить деякий тип до категорії структурних типів, скористайтеся перевіркою наступного вигляду:

    Sub Maine)

    Dim а As Integer = 3

    Console.Writel_ine("а is а value type is " & Isvaluetype(a))

    Console. Readline()

    End Sub

    Function Isvaluetype(Byval thing As Object) As Boolean

    Return (Typeof (thing) Is System.ValueType)

    End Function


    Ймовірно, перед нами одна з помилок розробників VB .NET — функція Typeof не може викликатися для структурних змінних без визначення допоміжної функції, одержуючої об'єкт вказаного типу. Звичайно, слід було б дозволити програмістові передавати структурний тип при виклику Typeof.

    Основні методи класу Object

    Оскільки клас Object є загальним предком всіх типів VB .NET, мабуть, що вам доведеться часто використовувати (або перевизначати) методи цього класу. Основні методи Object описані в декількох найближчих розділах.

    Досить часто виникає бажання перевизначити захищений метод Finalize класу Object. Теоретично код перевизначеного методу Finalize виконується при звільненні пам'яті, займаної об'єктом, в процесі збірки сміття. На практиці використовувати цей метод небажано. Оскільки ви не знаєте, коли і в якій послідовності будуть викликані методи Finalize, використовувати їх для дєїніциаліза-циі класів в кращому разі ненадійно. Замість цього слід реалізувати метод Dispose, описаний в розділі «Idisposable» цього розділу. А якщо ви все ж таки перевизначаєте метод Finalize, врахуйте, що в нім необхідно викликати Mybase.Finalize і продублювати весь код з методу Dispose.

    Equals і Referenceequals

    У класі Object підтримуються дві версії Equals — загальна і звичайна. Загальна версія має наступний синтаксис:

    Overloads Public Shared Function Equals(0bject. Object) As Boolean

    Приклад використання:

    Equals(а. b)

    Синтаксис звичайної версії:

    Overloads Over-ridable Public Function Equals(Object) As Boolean

    Приклад використання:

    а.Equals(b)

    Обидві версії методу Equal s перевіряють, чи володіють два об'єкти однаковими даними, але ви маєте бути готові перевизначити Equals, якщо цього вимагає специфіка вашого класу. Не забувайте, що загальні члени класу не перевизначаються, тому перевизначення допускається лише для звичайної (не загальною) версії Equal s.

    Наприклад, якщо у вашій програмі передбачено два способи представлення деякого структурного типу, поклопочіться про те, щоб ця обставина враховувалася методом Equals (саме так розробники VB .NET поступили з класом String, хоча, строго кажучи, цей клас не відноситься до структурних типів).

    У класі Object також передбачений загальний (і тому не перевизначуваний) метод Referenceequals. Метод Referenceequals перевіряє, чи представляють дві змінні один екземпляр. Наприклад, як показує наступний фрагмент, для двох рядків а і b вираз а.Equals(b) може бути істинним, а вираз Reference-equals (а. b) — помилковим:

    Sub Main()

    Dim а As String = "hello"

    Dim b As String = "Hello"

    Mid(b.l.d= "h"

    Console.Writeline("Is а.Equals(b)true?" & а.Equals(b))

    Console.WriteLine("Is Referenceequals(a.b) true?" & _

    Referenceequals(a.b))

    Console. Readline()

    End Sub

    Результат показаний на мал. 5.4.

    Мал. 5.4. Відмінності між методами Equals і Referenceequals

     




    :: Наша кнопка ::

    Отримати код:

    Підтримайте наш сайт і розмістіть нашу кнопку на своєму ресурсі.


    :: Реклама ::

    Скачати безкоштовно програму Microsoft Front Page 2003


    :: Посилання ::

    -


     

     

     


    Copyright ©