:: Меню ::

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

    :: Друзі ::

     
     

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

    = =

     

     

     

     

    Метод Tostring

    Метод Tostring повертає представлення поточного об'єкту в строковому форматі. Питання про те, чи буде це уявлення зручним при відладці і для користувачів, залежить від реалізації класу. За умовчанням Tostring повертає повне ім'я типу для заданого об'єкту — наприклад, System. Object або Examplel.Programmer.

    Постарайтеся звикнути до перевизначення Tostnng у ваших класах, щоб цей метод повертав змістовніше строкове представлення класу. Наприклад, в класі Employee з програми Employeetestl, приведеної в розділі 4, метод Tostring може виглядати приблизно так:

    Public Overrides Function Tostring() As String

    Dim temp As String

    temp = Me.GetType.ToString()& "my name is " & Me.TheName

    Return temp

    End Function

    Зразковий результат:

    Employeetestl+employeetestl+employee my name is Tom

    Функція Gettype і рефлексія

    Кожен тип .NET Framework представлений об'єктом Турі. Клас Турі містить безліч методів з складними іменами — наприклад, метод Getmembers повертає інформацію про імена всіх методів заданого класу. Метод Gettype класу Object повертає об'єкт Турі, за допомогою якого можна отримати інформацію про тип під час виконання програми. Зокрема, ця надзвичайно корисна можливість використовується для виконання рефлексії (також використовується термін «ідентифікація типів на стадії виконання»). До речі, простір імен Reflection займає таке важливе місце в роботі .NET Framework, що воно автоматично імпортується в кожен проект VS IDE.

    Щоб побачити, як виконується рефлексія, включите в проект посилання на збірку System.Windows.Forms і запустите приведену нижче програму. Коли через короткий проміжок часу на екрані з'явиться запрошення, натисніть клавішу Enter. Продовжуйте натискати Enter, і поступово в консольному вікні буде виведена інформація про всіх членів класу Windows. Forms. Form, на основі якого будуються графічні застосування в .NET. Зразковий вид вікна показаний на мал. 5.5.

    Мал. 5.5. Інформація про членів класу Windows.Forms.Form, отримана за допомогою рефлексії

    У цій програмі ми обмежуємося простим визовомtostring, але об'єкти Memberlnfo містять значно більше корисної інформації. За додатковими відомостями звертайтеся до електронної документації.

    1 Option Strict On

    2 Imports System.Windows.Forms

    3 Module Modulel

    4 Sub Main()

    5 Dim aform As New Windows.Forms.Form()

    6 Dim а Type As Type

    7 а Type = aform.GetType()

    8 Dim member As Object

    9 Console.Writellne("This displays the members of the Form class")

    10 Console.WriteLineC'Press enter to see the next one.")

    11 For Each member In atype.GetMembers

    12 Console.ReadLine()

    13 Console. Write(member.ToSthng)

    14 Next

    15 Console.WriteLine("Press enter to end")

    16 Console.ReadLine()

    17 End Sub

    18 End Module

    У рядках 6 і 7 ми отримуємо об'єкт Турі для класу Windows. Forms. Form. Потім, оскільки метод Getmembers класу Турі повертає колекцію об'єктів Memberlnfo, що описують члени класу, програма просто перебирає всі елементи колекції в рядках 11-14.

    Заміните Windows.Forms.Form іншим класом, і ви отримаєте інформацію про членів цього класу. Для отримання об'єкту Турі також можна передати повне ім'я класу в строковому форматі версії Gettype, оформленої у вигляді загального методу класу Турі. Рефлексія дозволяє виконувати пізнє скріплення в VB .NET — методу Invokemember передається рядок з інформацією про метод, що викликається (ймовірно, отриманою за допомогою рефлексії). За додатковими відомостями про цю можливість звертайтеся до опису класу Турі в документації .NET.

    Memberwiseclone

    У програмуванні, як і в сучасній науці:

    • Клоном називається точна копія об'єкту.
    • Стан клона може змінитися і стати відмінним від стану початкового об'єкту.

    Але найважливіше правило клонування формулюється так:

    • Зміни в клоні не повинні відбиватися на початковому об'єкті, на основі якого клон створювався.

    Останню обставину утрудняє клонування у всіх мовах ООП, тому ме-тод Memberwiseclone вважається потенційно за небезпечний. Річ у тому, що об'єкт може містити інші об'єкти. Якщо внутрішні об'єкти не будуть клоновані одночасно з об'єктом, що їх містить, замість пари оригінал-клон ви отримаєте сіамських близнят, які залежатимуть один від одного. Якщо клас містить поля, які є змінними об'єктами, метод Memberwiseclone свідомо створює «сирий», неповноцінний клон (це називається поверхневим копіюванням). Метод Memberwiseclone успішно клонує тільки ті об'єкти, поля яких відносяться виключно до структурних типів.

    Наступний приклад наочно показує, що мається на увазі під цим попередженням. Масиви VB .NET на відміну від масивів Vb6 є об'єктами.

    Допустимо, ми намагаємося клонувати об'єкт класу, одне з полів якого є масивом:

    1 Public Class Embeddedobjects

    2 Private m_data() As String

    3 Public Sub New(Byva1 anarray() As String)

    4 m_data = anarray

    5 End Sub

    6 Public Sub Oisplaydata()

    7 Dim temp As String

    8 For Each temp In m_data

    9 Console.WriteLine(temp)

    10 Next

    11 End Sub

    12 Public Sub Changedata(Byval newdata As String)

    13 m_data(0) = newdata

    14 End Sub

    15 Public Function Clone() As Embeddedobjects

    16 Return Ctype(Me.MemberwiseClone. Embeddedobjects)

    17 End Function

    18 End Class

    Виконаєте наступну процедуру Sub Main:

    Sub Main()

    Dim anarray() As String ={"HELLO"}

    Dim а As New Embeddedobjects(anarray)

    Console.WriteLinet"Am going to display the data in object а now!")

    а.DisplayData()

    Dim b As Embeddedobjects

    b =a.Clone()

    Dim newdata As String ="GOODBYE"

    b.ChangeData(newdata)

    Console.WriteLine("Am going to display the data in object b now!")

    b.DisplayData()

    Console.WriteLine("Am going to re-display the data in а" & _

    "after making а change to object b!!!") а.DisplayData()

    Console. Readline() End Sub

    Мал. 5.6. Метод Memberwiseclose не працює

    Як видно з мал. 5.6, результат вийшов вельми несподіваним: зміни клона відбиваються на початковому об'єкті!

    Що відбувається в даному прикладі? Чому метод Memberwiseclone не працює, як задумано? Чому зміни в об'єкті b відбиваються на об'єкті а? Тому що в рядках 2 і 4 класи Embeddedobjects як значення поля, що задається в конструкторі, використовується масив. Масиви є змінними об'єктами; як було показано в розділі 3, з цього виходить, що вміст масиву може змінюватися навіть при передачі за значенням (Byval). Стан внутрішнього масиву змінюється в рядках 12-14 класу Embeddedobjects. Оскільки об'єкт і псевдоклон зв'язані посиланням на масив m_data, зміни клона відбиваються на початковому об'єкті.

    Вирішення цієї проблеми розглядається в розділі «Icloneable» цього розділу. А поки ми просто вкажемо, що справжній клон (іноді званий глибокою копією) створює клони всіх полів об'єкту, при необхідності виконуючи рекурсивне кло-нірованіє. Наприклад, якщо одне з полів класу є об'єктом і містить ще один внутрішній об'єкт, процес клонування повинен опуститися на два рівні в глиб.

    Також існує хитромудра методика клонування, заснована на серіалізациі об'єктів. Подробиці приведені в розділі 9.

    Нарешті, як засіб додаткового захисту розробники .NET Framework оголосили Memberwiseclone захищеним методом класу Object. Як було показано вище, це означає, що Memberwi seci one може викликатися тільки з похідних класів. Код за межами похідного класу не може клонувати об'єкти за допомогою цього небезпечного методу. Також звернете увагу на те, що Memberwi secione повертає тип Object, тому в рядку 1б класу Embeddedobjects доводиться використовувати функцію Стуре.

    Проблема нестійкості базових класів і контроль версії

    Проблема несумісності компонентів добре відома всім, кому доводилося програмувати для Windows. Зазвичай вона виступає у формі так званого кошмару DLL (DLL Hell) — програма використовує певну версію DLL, а потім установка нової версії компоненту порушує роботу програми. Чому? Причини можуть бути разнимі, від очевидних (випадкове виключення функції, що використалася в програмі) до вельми нетривіальних (наприклад, зміна типу повертаного значення у функції). У будь-якому випадку все зводиться до варіацій на одну тему — при зміні відкритого інтерфейсу коди, від якої залежить ваша програма, програма не може використовувати нову версію замість старої, а стара версія вже стерта. У більшості об'єктно-орієнтованих мов спадкоємство зв'язане з потенційною загрозою працездатності вашої програми із-за несумісності компонентів. Програмістові залишається лише сподіватися на те, що відкриті і захищені члени класів-попередників в 1 ієрархії спадкоємства не змінюватимуться, таким чином, що це порушить ра-

    ботоспособность їх програм. Ця ситуація називається проблемою нестійкості базових класів. Спадкоємство часто перетворює наші програми на якусь подібність карткового будиночка — спробуйте витягнути нижню карту, і вся споруда розвалиться.

    Проблему нестійкості базових класів бажано розглянути на конкретному прикладі. Розмістите приведене нижче визначення класу Payabl eentity в отдель-ной^бібліотеке і відкомпілюйте його в збірку з ім'ям Payableentity Example командою Build (щоб задати ім'я збірки, клацніть правою кнопкою миші на імені проекту у вікні рішення, виберіть в контекстному меню команду Properties і введіть потрібні значення в діалоговому вікні). Якщо ви не використаєте архів з прикладами, що додається до книги, запам'ятаєте, в якому каталозі був побудований проект:

    Public Mustlnherit Class Payableentity

    Private m_name As String

    Public Sub New(Byval thename As String)

    m_name =theName

    End Sub

    Public Readonly Property Thename()As String Get

    Return m_name

    End Get

    End Property

    Public Mustoverride

    Property TAXID()As

    String End Class

    Після побудови DLL закрийте рішення.

    Допустимо, ви вирішили включити в клас Employee новий спосіб отримання адреси, залежний від базового класу Payableentity; при цьому слід пам'ятати, що клас використовуватиметься тільки у формі, що відкомпілювалася. Для цього необхідно включити посилання на збірку, що містить цей проект (знаходиться в підкаталозі \bin того каталога, в якому була побудована DLL Payableentityexample). Зразковий код класу Empl oyee приведений нижче. Звернете увагу на рядок, виділений жирним шрифтом, в якій клас оголошується похідним від абстрактного класу, визначеного в збірці

    Payableentityexample.

    Public Class Employee

    ' Простір імен називається Payableentityexample.

    ' тому повне ім'я класу записується у вигляді

    Payableentityexample.PayableEntity! Inherits

    Payableentityexample.Employee

    Private m_name As String

    Private m_salary As Decimal

    Private m_address As String

    Private m_taxid As String

    Private Const LIMIT As Decimal = 0.1d

    Public Sub New(Byval thename As String

    Byval cursalary As Decimal

    Byval TAXID As String)

    Mybase.New(thename)

    m_name = thename

    m_salary = cursalary

    m_taxid = TAXID

    End Sub

    Public Property Address()As String

    Get

    Return m_address

    End Get

    Set(Byval Value As String)

    m_address = Value

    End Set

    End Property

    Public Readonly Property Salary()As Decimal Get

    Return m_salary «

    End Get

    End Property

    Public Overrides Property TAXIDO As String Get

    Return m_taxid

    End Get

    Setcbyval Value As String)

    If Value.Length <> 11 Then

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

    m_taxid = Value

    End If

    End Set

    End Property

    End Class

    Процедура Sub Main виглядає так:

    Sub Main()

    Dim torn As New Employeec'tom". 50000)

    tom.Address ="901 Grayson"

    Console.WriteCtom.TheName & "lives at " & tom.Address)

    Console. Readline()

    End Sub

    Результат показаний на мал. 5.7. Програма працює саме так, як передбачалося.

    Мал. 5.7. Демонстрація нестійкості базових класів (контроль версії відсутній)

    Програма компілюється у виконуваний файл Versiomngl.exe, все йде чудово.

    Тепер припустимо, що клас Payableentity був розроблений незалежною фірмою. Геніальні розробники класу Payableentity не бажають опочивати на лаврах! Піклуючись про благо користувачів, вони включають в свій клас об'єкт з адресою і розсилають новий варіант DLL. Початковий текст вони тримають в секреті, але ми його приводимо нижче. Зміни в конструкторі виділені жирним шрифтом:

    Imports Microsoft.Vi sualbasic.Control Chars

    Public Class Payableentity

    Private m_name As String

    Private m_address As Address

    Public Sub New(Byval thename As String,byval theaddress As Address)

    m_name = thename

    m_address = theaddress

    End Sub

    Public Readonly Property Thename()As String Get

    Return m_name End Get

    End Property

    Public Readonly Property Theaddress() Get

    Return

    m_address.DisplayAddress

    End Get

    End Property

    End Class

    Public Class Address

    Private m_address As String

    Private m_city As String

    Private m_state As String

    Private m_zip As String

    Public Sub New(Byval theaddress As String.ByVal thecity As String.

    Byval thestate As String.ByVal thezip As String)

    m_address = theaddress

    m_city = thecity

    m_state = thestate

    m_zip = thezip

    End Sub

    Public Function Displayaddress() As String

    Return m_address & Crlf & m_city & "." & m_state _

    &crlf & m_zip

    End Function

    End Class

    Перед вами приклад рідкісної халтури. В процесі «удосконалення» автори вмудрилися втратити початковий конструктор класу Payableentity! Звичайно, такого бути не повинно, але раніше подібні катастрофи все ж таки траплялися. Стара DLL встановлювалася на жорсткий диск користувача (зазвичай в каталог Windows\system). Потім виходила нова версія, встановлювалася поверх старої, і цілком благополучна програма Versioningl переставала працювати (а як їй працювати, якщо змінився конструктор базового класу?).

    Звичайно, проектувальники базових класів так поступати не повинні, проте на практиці бувало всяке. Але спробуйте відтворити цей приклад .NET, і відбудеться справжнє диво: ваша стара програма нормально працюватиме, тому що вона використовує початкову версію Payabl eenti ty з бібліотеки, що зберігається в каталозі \bin вирішення Versioningl.

    Вирішення проблеми несумісності версій в .NET Framework кінець кінцем основа-но на тому, що ваш клас знає версію DLL, необхідну для його роботи, і відмовляється працювати за відсутності потрібної версії. Успішна робота цього механізму залежить від особливих властивостей складок (див. розділ 13). Не тим менєє.в описаної нами ситуації захист .NET Framework долається простим копіюванням нової DLL на місце старої.

    Схема контролю версії в .NET дозволяє розробникам компонентів доповнювати свої базові класи новими членами (хоча на практиці робити цього не рекомендується). Така можливість зберігається навіть в тому випадку, якщо імена нових членів збігаються з іменами членів, включених вами в похідний клас. Старий виконуваний файл, створений на базі похідного класу, продовжує працювати, оскільки він не використовує нову DLL.

    Втім, це не зовсім вірно: він дійсно продовжує працювати — до тих пір, поки ви не відкриєте початковий текст додатку Versioningl в VS .NET, створите посилання на DLL Payableentityexample і спробуєте побудувати додаток Versioningl наново. Компілятор видасть повідомлення про помилку:

    C:\book to comp\chapter 5\versioningl\versioningl\modu1el.vb(21):

    No argument specified or non-optional parameter 'theaddress' of

    'Public Sub New(thename As String,theaddress

    As Payableentityexample.Address)'.

    Отже, як тільки ви завантажите старий початковий текст похідного класу і створите посилання на нову DLL, вам не вдасться відкомпілювати програму до виправлення тієї несумісності, на яку вас прирекли розробники базового класу.

    Перш ніж завершити цей розділ, ми хочемо роз'яснити ще одну обставину. Виключення конструктора з класу і заміна його іншим конструктором — вельми груба і очевидна помилка. Чи здатний механізм контролю версії .NET врятувати від інших, менш тривіальних помилок? Так, здатний.

    Розглянемо найпоширеніше (хоча досить тривіальний) джерело помилок несумісності при використанні спадкоємства. Є похідний клас Derived, залежний від базового класу Parent. У клас Derived включається новий метод Parselt (у наступному прикладі він просто розділяє рядок по словах і виводить кожне слово в окремому рядку):

    Imports Microsoft.VisualBasic.ControlChars Module Modulel

    Submain()

    Dim myderived As New Oerived()

    myderived.DisplayIt 0

    Console.ReadLine()

    End Sub

    End Module

    Public Class Parent

    Public Const MY STRING As String ="this is а test"

    Public Overridable Sub Displaylt()

    Console.WriteLine(My_string)

    End Sub

    End Class

    Public Class Derived Inherits Parent

    Public Overrides Sub Displaylt()

    Console.WriteLine(Parseit(Mybase.MY_STRING))

    End Sub

    Public Function Parselubyval astring As String)

    Dim tokens() As String

    ' Розбити рядок по пропусках tokens -

    astring.Split(Chr(32))

    Dim temp As String

    ' Об'єднати в один рядок, вставляючи між словами

    ' комбінацію символів Cr/lf

    temp = Join(tokens.CrLf)

    Return temp

    End Function

    End Class

    End Module

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

    Мал. 5.8. Просте розбиття рядка по словах

    Тепер уявіть собі, що клас Parent розповсюджується не у вигляді початкових текстів, а у формі, що відкомпілювалася. Версія 2 класи Parent містить власну версію Parselt, яка широко використовується в її коді. Відповідно до принципу поліморфізму при зберіганні об'єкту типу Den ved в об'єктній змінній типу Parent виклики Displaylt повинні використовувати метод Parselt класу Derived замість методу Parselt базового класу. Проте тут виникає маловірогідна, але теоретично можлива проблема. У нашому сценарії код класу Parent, що використовує свою версію функції Parselt, не знає, як функція Parselt реалізована в класі Derived. Поліморфний виклик версії Parselt похідного класу може порушити які-небудь умови, необхідні для роботи базового класу.

    У цій ситуації засобу контролю версії VB .NET теж творять дива: код базового класу Parent, що відкомпілювався, продовжує використовувати свою версію Parselt завжди, навіть не дивлячись на те, що при зберіганні об'єктів Derived в змінних типу Parent поліморфізм привів би до виклику неправильної версії методу. Як згадувалося в попередньому прикладі, при відкритті коди Derived в Visual Studio компілятор повідомляє, що для усунення неоднозначності в оголошення методу Parselt похідного класу слід включити ключове слово Override або Shadows.

     




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

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

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


    :: Реклама ::

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


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

    -


     

     

     


    Copyright ©