Пространства имен

При использовании кода, написанного другими программистами, всегда возникает потенциальная опасность конфликтов имен. Но даже если забыть об этих конфликтах, все равно необходимо каким-то образом сгруппировать тысячи методов в соответствии с их функциональным назначением. Следовательно, в огромной библиотеке классов .NET должен существовать какой-либо способ логической группировки информации. В .NET эта задача решается при помощи пространств имен. Конечно, вы можете создавать собственные пространства имен; ниже в этой главе мы покажем, как это делается.

В каждом городе есть своя Главная Улица, а в каждой библиотеке непременно найдется метод с именем Open. Пространства имен позволяют различать эти методы. Например, в пространстве имен System. 10 собраны методы для выполнения файловых операций; в него входит класс Fil е, а в этом классе имеется метод Open. Полное имя метода выглядит так:

System.I0.File.Open

Класс File является частью пространства имен System. I0, поэтому он не конфликтует с другим классом File из пространства имен Cornell .Morrison.NiftyClasses, который также может содержать собственный метод Open.

 

Импортирование

Обращаясь к хорошим знакомым, мы не называем их по имени-отчеству и фамилии. В .NET предусмотрена возможность импортирования пространств имен командой Imports, что позволяет избавиться от громоздких полных имен. После правильной команды Imports все ссылки вида

System.Text.StringBuilder .

могут заменяться простым именем StringBuilder.

Пространство имен System автоматически импортируется в каждое решение, созданное в Visual Studio. Благодаря этому методы этого пространства имен могут вызываться в виде Console.WriteLine() вместо полного имени System.Console.WrlteLlne().

Список пространств имен, автоматически импортируемых в решение, находится на странице Imports окна свойств решения (рис. 4.2).

Загрузите в Object Brewser автоматически импортируемое пространство имен Microsoft. Visual Basic — вы увидите, что оно содержит различные функции, существовавшие в Visual Basic и сохраненные в VB .NET (рис. 4.3).

Импортирование пространства имен Microsoft. Visual Basic. Constants позволяет использовать старые константы VB — такие, как vbCrtf. .NET-версии многих констант не имеют префикса vb (например, CrLf) и находятся в пространстве имен

Microsoft.VIsualBasi с.Control Chars.

Команда Imports должна располагаться перед всеми остальными объявлениями, включая объявление имени модуля, но после директив Option (таких, как Option Strict On или Option Compare Text).

Рис. 4.2. Страница Imports окна свойств решения

Рис. 4.3. Пространство имен Microsoft.VisualBasic в программе Object Browser

Команда Imports не увеличивает объем программы, поскольку она не включает весь код пространства имен в проект, а просто упрощает ссылки на члены классов, входящих в пространство имен. На скорость работы она тоже не влияет. Импортируются только пространства имен, входящие в сборки, ссылки на которые были включены в программу командой Project > Add Reference (сборки рассматриваются в главе 13).

Мы были просто поражены, когда узнали, что подсказка IntelliSense работает и для команды Imports — она выводит список пространств имен, которые можно импортировать в проект. Для этого IDE анализирует сборки, ссылки на которые имеются в решении.

Если два импортированных пространства имен содержат классы с одинаковыми именами, то для различения этих классов вам придется указывать их полные имена (VB .NET не позволяет создать два одноименных класса в одном пространстве имен).

Особая версия команды Imports используется для предотвращения потенциальных конфликтов имен с ранее импортированными классами. Например, если вы захотите работать на уровне совместимости с прежними версиями Visual Basic (чего делать не рекомендуется), в программе почти наверняка возникнут конфликты имен. Включите в программу ссылку на уровень совместимости VB, а затем воспользуйтесь командой вида:

Imports VB6Stuff = Microsoft.VisualBasic.Compatibility.VB6

После этого остается лишь начинать все ссылки на уровень совместимости с префикса «VBSStuff.», и все проблемы с конфликтами имен исчезнут.

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

Imports System.IO.Directorylnfo

 

Класс DirectoryInfo

Чтобы рассмотреть пример использования Imports на сколько-нибудь нетривиальном примере, мы возьмем класс Directorylnfo из пространства имен System. IO. Как подсказывает само название, класс Directorylnfo содержит методы для получения информации о содержимом каталогов, вывода полного имени каталога и т. д. Один из конструкторов этого класса получает строку с именем каталога, который вы хотите проанализировать (если переданное имя не является абсолютным, конструктор считает, что оно задается относительно текущего каталога программы). Правильно написанная команда Imports позволяет заменить длинную команду Dim dirlnfo As New System.IO.Directory!nfo("C:\") более компактной и понятной командой

Dim dirlnfo As New DirectoryInfo("C:\")

Следующая программа выводит список всех каталогов на жестком диске, в ее работе используются рекурсия и класс Directorylnfo. Ключевую роль в ней играет метод GetDi rectories (), возвращающий коллекцию подкаталогов. Функция ListDi rectories перебирает содержимое коллекции и рекурсивно вызывается для каждого элемента:

Option Strict On Imports System.IO Module Modulel

Sub Main())

Dim dirlnfo As New DirectoryInfo("C:\")

ListDirectories(dirInfo)

End Sub

Sub ListDirectories(ByVal theDirectory

As Directorylnfo)

Dim tempDir*As DirectoryInfo

Console. Wri.teLi net theDi rectory .Full Name())

For Each terrain In theDi rectory. GetDi rectories ()

ListDirectories(tempOir) Next End Sub End Module

Если вы привыкли к рекурсивному перебору каталогов с использованием старой функции Dir, вы оцените, насколько упростил эту программу в .NET замечательный метод Directorylnfo.

Во время работы над этим примером мы легкомысленно назвали свое решение D1-rectorylnfo. В результате команда Imports перестала работать! Причины так и остались неизвестными, но мораль ясна: не присваивайте своим решениям имена, совпадающие с именами классов библиотеки .NET.

 

Справочная система и .NET Framework

В библиотеку .NET Framework входят сотни пространств имен, каждое из которых содержит множество полезных классов. По масштабам и возможностям .NET Framework сравнима с полным интерфейсом Win32 API. Библиотека настолько огромна, что описать ее в одной книге попросту невозможно. Хотя эта глава дает начальное представление о некоторых классах .NET Framework, как можно скорее приступайте к чтению документации .NET. Начните с раздела «.NET Framework Class Library» и найдите описания пространств имен, представляющих для вас интерес. Как показано на рис. 4.4, в справочной системе перечислены все классы каждого пространства имен.

Каждое имя класса в левом столбце представляет собой гиперссылку, ведущую к подробному описанию класса. В нижней части описания класса перечислены имена его членов. Если щелкнуть на любом из этих имен, вы перейдете к подробному описанию соответствующего члена. Обратите внимание: VB .NET уже не считается второстепенным языком — синтаксис всех членов приводится для VB, VC и С#. На рис. 4.5 показан пример документации класса Directorylnfo в бета-версии 2.

Чтобы получить подробное описание метода GetDi rectories, использованного в предыдущем примере, щелкните на ссылке Directorylnfo в нижней части страницы, а затем щелкните на ссылке GetDi rectories. Внешний вид страницы показан на рис. 4.6. Некоторые термины, встречающиеся на этой странице (такие, как Pri vate), рассматриваются далее в этой главе,

 

Классы коллекций в .NET Framework

Чтобы пробудить в вас интерес к .NET Framework, мы кратко рассмотрим некоторые классы коллекций. В этих классах реализуются стандартные структуры данных, часто используемые в нетривиальных программах. Коллекции настолько важны, что они по умолчанию автоматически импортируются в каждое решение VB .NET (в пространстве имен System.Collections).

Рис. 4.4. Пространство имен System.10 в справочной системе

В совокупности эти классы оставляют далеко позади примитивный класс Collection из VB6. Самые полезные классы коллекций перечислены в табл. 4.2. В следующих двух разделах рассматриваются основные принципы работы с двумя важнейшими классами: ArrayList и HashTable. Знакомство с очень важным классом Dictio-naryBase откладывается до следующей главы, посвященной наследованию.

Таблица 4.2. Основные классы коллекций

Имя класса

Описание

ArrayList Динамический массив, размеры которого увеличиваются и уменьшаются по мере надобности
BitArray Используется для поразрядных операций с отдельными битами
Hashtable Коллекция пар «ключ/значение», упорядоченная.,по хэш-кодам ключей
Queue Очередь (принцип FIFO, «первым пришел, первым вышел»)
Stack Стек (принцип LIFO, «последним пришел, первым въ:шел»)
DictionaryBase Базовый класс для различных ассоциативных массивов (словарей). В ассоциативном массиве хранятся пары «ключ/значение», и работать с ними удобнее, чем со многими типами коллекций. Класс DictionaryBase используется только путем наследования (см. главу 5)

Рис. 4.5. Класс DirectoryInfo в справочной системе

 

ArrayList

Класс ArrayList реализует динамический массив, размеры которого автоматически увеличиваются и уменьшаются по мере надобности. Динамические массивы работают чуть медленнее обычных массивов, но они заметно упрощают многие задачи программирования. Кроме того, в отличие от большинства массивов класс ArrayLi st является гетерогенным, то есть позволяет хранить объекты разных типов. В главе 5 будет показано, как создать класс ArrayList для хранения объектов лишь одного типа; вы также узнаете о некоторых нюансах, связанных с хранением обобщенных объектов в ArrayLi St.

Использование ArrayList вместо базового массива означает, что вам не придется часто вызывать ReDim Preserve для сохранения существующих данных. Достаточно вызвать метод Add, и класс ArrayList сам выполнит всю черновую работу. Класс ArrayList содержит ряд других полезных методов. Например, метод AddRange позволяет перенести в динамический массив все содержимое существующего массива всего одной командой. После завершения обработки элементы можно скопировать обратно. В частности, это позволяет легко объединить содержимое двух массивов. В табл. 4.3 перечислены основные члены класса ArrayList (полный список приведен в электронной документации).

Рис. 4.6 . Метод GetDirectories в справочной системе

Таблица 4.3. Важнейшие члены класса ArrayList

Имя

Описание

Copy To Копирует объект ArrayList (полностью или частично) в одномерный массив начиная с заданного индекса массива-приемника
Contains Проверяет, присутствует ли в объекте ArrayList заданный элемент
Clear

Удаляет все элементы из объекта ArrayList

Capacity Получает или задает максимальное количество элементов, на которое рассчитан объект ArrayList. Конечно, вместимость массива изменяется по мере добавления новых элементов, но по соображениям эффективности вместимость наращивается большими «порциями»
BinarySearch Выполняет бинарный поиск заданного элемента в отсортированном динамическом массиве или в его части
AddRange

Позволяет добавить содержимое другого массива (динамического или обычного) в текущий динамический массив. В сочетании с методом InsertRange позволяет быстро объединять массивы с использованием Arraylist в качестве вспомогательного класса

Add
Добавляет новый объект в конец динамического массива

Имя

Описание

Count

Возвращает количество элементов, фактически хранящихся в массиве

GetRange

Возвращает другой объект ArrayList, содержащий последовательность смежных элементов текущего объекта

IndexOf

Возвращает индекс первого вхождения заданного элемента в динамический массив. Следует помнить, что индексация в классе ArrayList (как и в обычных массивах) начинается с нуля

Insert

Вставляет элемент в заданную позицию объекта ArrayList

InsertRange

Вставляет элементы коллекции в объект ArrayList начиная с заданной позиции

Item

Получает или задает значение элемента, находящегося в заданной позиции. Является свойством по умолчанию для класса ArrayList

LastlndexOf

Возвращает индекс последнего вхождения заданного элемента в динамический массив (индексация начинается с нуля)

Length

Возвращает количество элементов в динамическом массиве

Readonly

Возвращает новый объект ArrayList, доступный только для чтения (проверка возможности записи в динамический массив осуществляется методом IsReadOnly)

Remove

Удаляет из массива первое вхождение заданного элемента

Re move At

Удаляет элемент, находящийся в заданной позиции

RemoveRange

Удаляет последовательность смежных элементов

RepeatRange

Возвращает объект ArrayList, содержащий заданное количество дубликатов


одного элемента

Reverse

Переставляет элементы в объекте ArrayList в противоположном порядке (во всем массиве или в его части)

SetRange

Копирует элементы коллекции поверх интервала элементов ArrayList

Sort

Сортирует элементы в объекте ArrayList (во всем массиве или в его части)

ToArray

Копирует элементы из объекта ArrayList в массив

TrimToSize

Используется после завершения операций с объектом ArrayList; вместимость динамического массива уменьшается до фактического количества элементов, хранящихся в нем в настоящий момент (разумеется, позднее массив снова может увеличиться)

Среди свойств класса ArrayList наибольший интерес представляет свойство Item, которое представляет элемент с заданным индексом. Пример:

Consolе.WriteLinediiyList.Item( 1))

Свойство Item является свойством по умолчанию класса ArrayList. Это означает, что при использовании его имя может не указываться, Например, приведенная выше команда эквивалентна следующей команде:

Console. WriteLine(myList(1))

В разделе «Свойства» настоящей главы вы узнаете, чем отличаются свойства по умолчанию в VB .NET и прежних версиях VB.

В следующем коротком примере массив ArrayLi st используется для ввода и сохранения неизвестного количества строк. При этом удается обойтись без команды ReDim Preserve, необходимой при работе с обычными массивами.

Option Strict On Module Modulel

Sub Main()

Dim myList As New ArrayList()

Dim theData As String

Console.Write("Please enter each item and hit Enter key,"_

& "enter ZZZ when done:") theData =Console.ReadLine()

Do Until theData ="ZZZ" myList.Add(theData)

Console.WriteC'Please enter each item and hit Enter,"_

& "enter ZZZ when done:") theData =Console.ReadLine() Loop

Console.WriteLine("You entered "SmyList.Count() & "ITEMS.")

Console.ReadLine()

End Sub

End Module