|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Хэш-таблицы
Прості і динамічні
масиви зручні перш за все тим, що ви можете безпосередньо звернутися
до будь-якого елементу по індексу. Звичайно, для цього необхідно знати індекс. У
наступній структурі даних — хэш-таблице — довільний доступ до даних
здійснюється по ключу. Допустимо, у вас є хэш-таблица з ім'ям
thedata. Команда thedata("Bill 's Address")
дозволяє витягувати з хэш-таблицы потрібний елемент без циклічного перебору
всього вмісту. Хэш-таблицы дуже зручні в ситуаціях, коли ви хочете дістати
швидкий доступ до значення по пов'язаному з ним унікальному атрибуту, тобто ключу.
Зрозуміло, програмування хэш-таблицы — завдання непросте [ Для цього необхідно
побудувати хорошу функцію хешування для обчислення індексу даних по ключу, а
також вирішити неминучу проблему колізій, тобто збіги хэш-кодов у двох різних
елементів. Навіть термінологія виглядає страхітливо... ], але, на щастя, ця
робота вже виконана за вас розробниками .NET Framework. У таблиці. 4.4
перераховані найважливіші методи класу Hashtable (за повним списком звертайтеся
до електронної документації). Таблиця
4.4.
Найважливіші методи класу Hashtable
За допомогою
класу Hashtable можна зберегти інформацію, отриману при виклику методу Getenvironmentvariables
класу System. Environment. Приведена нижче невелика програма виводить імена
і значення всіх змінних оточення, визначених в системі. Програму можна
завершити у будь-який момент, просто закривши консольне вікно. Спочатку проглянете
лістинг, а потім ми пояснимо пару неочевидних моментів: 1 Option
Strict On 2 Imports System.Environment 3 Module
Modulel 4 Sub Main() 5 Dim
evariables As Hashtable 6 evariables =CType(Getenvironmentvariables(). Hashtable) 7 Console.Writel_ine("Press Enter to see the next
item") 8 Dim
thing As Object 9 For Each thing
In evariables.Keys 10 Console.WriteLineC'The environment variable
named " & _ 11 thing. Tostring() & "has value " & evariables(thing).ToString()) 12 Console.
Readline() 13
Next 14 End
Sub 15 End Module Перш за все
використаний в рядку 6 спрощений синтаксис імені методу став можливим завдяки
виклику Imports в рядку 2: evariables =CType(Getenvironmentvariables(),Hashtable) Значення,
отримане при виклику Getenvironmentvariables(), перетвориться в хэш-таблицу
функцією Стуре [ Можливо, в майбутніх ієрсиях .NET таке перетворення працювати
не буде. ]. У рядках 8 і 9 для перебору елементів хэш-таблицы використовується
змінна типу Object: Dim
thing As Object For Each thing
In evariables.Keys У стандартних хэш-таблицах зберігаються тільки об'єкти. Але оскільки в VB .NET всі дані є об'єктними, строкові значення змінних оточення також можуть зберігатися в змінній thing. Програма перебирає вміст колекції Keys і за допомогою властивості Item для кожного ключа набуває асоційованого значення. Конструкцію evariables(thing) в рядку 11 також можна записати в наступному вигляді: evariables.Item(thing) У рядку
11 викликається метод Tostring, визначений в кожному класі (цей важливий метод
описаний в розділі 5). Тут цей метод використовується для виведення строкового представлення
ключа.
Розглянемо
наступний фрагмент: Dim
thing As New Object Dim
arandomlnstance As New Random У нім оголошуються і створюються дві змінні: thing і arandomlnstance. Перша змінна містить посилання на тип Object, а друга — посилання на екземпляр класу Random. Наступна команда цілком допустима навіть в режимі жорсткої перевірки типів (Option Strict On), оскільки в VB .NET всі змінні кінець кінцем є об'єктами: thing = arandomlnstance З іншого
боку, зворотне привласнення (arandomlnstance = thing) неприпустимо, оскільки
не кожен об'єкт є екземпляром класу Random. Об'єктну
змінну можна розглядати як маніпулятор блоку пам'яті (причому не
фіксованого, а переміщуваного). Об'єктні змінні також часто називають
посиланнями (references) або інтелектуальними покажчиками (smart
pointers). Зазвичай при використанні знаку = з ключовим словом New
маніпулятор зв'язується з блоком пам'яті, в якому зберігається відповідний об'єкт
(при роботі з так званими структурними типами виникають деякі
тонкощі, які розглядатимуться далі в цьому розділі). Sub Maln() Dim A As New
Arraylist() Dim В As
Arraylist У =
А B.Add("foo") Console.WriteLine(A.Count) Console.ReadLine()
End Sub
Динамічний масив А також міститиме рядок foo, тому виведене значення
A.Count дорівнюватиме 1. Оскільки
в VB .NET рядки і масиви є об'єктами, слід пам'ятати, що для роботи
з ними використовуються об'єктні змінні. Як було показано в розділі 3, це дозволяє
використовувати вбудовані можливості відповідних класів за допомогою синтаксису
«.». Наприклад, при роботі з масивом через змінну апаггау команда
anarray.Sort() відсортує масив надзвичайно ефективним методом швидкого сортування. Як і
в колишніх версіях VB, об'єктні змінні можуть використовуватися для отримання
компактнішого запису. Наприклад, в наступному фрагменті визначається коротке ім'я авох,
яке використовуватиметься замість довгого Му- Form.TextBoxl: Dim abox As
System.Windows.Forms.TextBox abox = Myform.TextBoxl Подібні
скорочення часто використовуються у поєднанні з ключовим словом With: With
abox .AutoSize =False .Height
=1000 .Width
=200 .Text ="Hello" End With
Оператор
Is перевіряє, чи посилаються дві об'єктні змінні на одну область пам'яті.
Наступний фрагмент в обох випадках виводить True, оскільки в результаті операцій
привласнення всі об'єктні змінні посилаються на одну область пам'яті: Dim Objectl
As New Object() Dim OBJECTZ
As New Object() Dim Objects
As New Object() OBJECTZ
=Object1 Objects
Object2 Console.WriteLine(Objectl
Is Object2) Console.WriteLine(Object1
Is Object3) Як і в колишніх
версіях VB, привласнення об'єктній змінній значення Nothi ng розриває її
зв'язок з блоком пам'яті. Коли об'єктна змінна рівна Nothing, вона не асоціюється
ні з яким об'єктом. У цьому стані знаходяться всі об'єктні змінні, які
були оголошені в програмі, але ще не ініціалізувалися. У програмі часто
зустрічаються перевірки наступного вигляду: If
anobject Is Nothing Then ' Змінна не пов'язана з об'єктом, привласнити значення Else ' Значення було привласнене раніше End
If Додаткова
інформація про те, що відбувається при привласненні об'єктним змінним значення
Nothing, приведена в розділі «Збірка сміття і завершення».
Функція Typename повертає рядок з описом типу. Для всіх типів, окрім базових,
має бути заздалегідь викликаний оператор New; інакше функція повертає рядок
Nothing. Наприклад, наступний фрагмент виводить в консольному вікні рядок Nothing: Dim ansbuilder
As System.Text.StringBuilder Console.WriteLineC'My
type name is " & Typename(ansbuilder)) Але після
виклику New у вікні буде виведений рядок Stringbuilder: Dim ansbuilder
As New System.Text.StringBuilder Console.WriteLineC'My
type name is " & Typename(ansbuilder)) Функція Typename
повертає коротке ім'я класу, тому не розраховуйте отримати повне ім'я
виду System.Text.StringBuilder. Якщо викликати
функцію Typename для масиву, ви отримаєте строкове ім'я, за яким слідує
порожня пара круглих дужок. Приклад: Dim
athing(5) As
Integer Console.WriteLine("My
type Harness " & Typename(athing)) Отриманий рядок
має вид Integer(). Функція Typename
зручна в процесі відладки, але в остаточних версіях програм зазвичай використовується
оператор Typeof...Is. Він працює набагато ефективніше, оскільки обходиться без
порівнянь рядків, необхідних при використанні Typename. Синтаксис перевірки
виглядає таким чином: If Typeof athing
Is System.Text.SthngBuilder Then ' Об'єкт
відноситься до типу Stringbuilder End If
Проблеми з передачею об'єктних змінних за значенням
Більшість
мов програмування вимагають чіткого розуміння, чим передача параметрів по
посиланню відрізняється від передачі за значенням. Не забувайте, що в VB .NET параметри
за умовчанням передаються за значенням (Byval). Більшість
програмістів керуються простим правилом: якщо параметр передавався по
посиланню, його зміни зберігаються в початковій змінній, а якщо за значенням
— зміни втрачаються після виходу з функції або процедури. На жаль, у випадку
з об'єктними змінними це правило не завжди істинно. Спробуйте виконати
наступний фрагмент, в якому масив передається в процедуру за значенням. Ви
переконаєтеся в тому, що початковий масив змінюється після виклику процедури! Module Modulel
Sub Main() Dim a() As String
={"HELLO"."GOODBYE"} Console.WriteLineC'Original
first item in array is:" & а(0)) Console.WriteLineC'Original
second item in array is:" & а(1)) Yikes(a)' Масив
передається за значенням! Console.WriteLineC'After
passing by value first item in array now is:"_ &A(0)) Console.WriteLine("After
passing by value second item in array is:"_ &АШ) Console. Readline() End Sub Sub Yikes(Byval
Foo As String()) Foo(0)= "GOODBYE" Food) = "HELLO" End Sub End Module
Мал.
4.7. Результат роботи
тестової програми Те,
що відбувається виглядає щонайменше дивно; ми передаємо масив за значенням, але зміни
чомусь відбиваються в початковій копії! У попередніх версіях VB це було б неможливо.
Отже, що відбувається? Головна причина
полягає в тому, що при передачі за значенням завжди створюється нова копія
початкової змінної; після виходу з функції ця копія знищується. Але, передаючи
за значенням об'єктну змінну, ви наказуєте VB .NET створити копію маніпулятора
для роботи з об'єктом. Усередині процедури операції з тимчасовим маніпулятором
відбиваються на вмісті цієї області пам'яті. Після виклику з процедури копія
знищується, але всі зміни у вмісті пам'яті залишаються в силі. Уявіть
собі валізу, до якої тимчасово приробили другу ручку. Ви перенесли валізу
за нову ручку на інше місце; навіть якщо тепер від'єднати ручку, валіза
все одно залишиться на новому місці. У цій дивній
ситуації є лише одне виключення — коли початковий об'єкт є незмінним
(immutable). Із стандартних, постійно використовуваних класів до цієї категорії
відноситься тільки клас Stri ng. В цьому випадку передача за значенням працює
саме так, як годиться, в чому неважко переконатися за допомогою наступної програми: Option Strict
On Module Modulel Sub Main() Dim A As
String = "hello" Noproblem(A) Console.WriteLine("After
passing by value the string is still " & A) Console. Readline() End
Sub Sub Noproblem(Byval
Foo As String) Foo = "goodbye" End Sub End Module
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||