В этой главе
представлен базовый «арсенал» VB .NET. Большая часть материала сводится
к краткому обзору концепций, присущих всем языкам программирования (например,
переменных и команд цикла), и описанию базовых типов данных, в основном различных
чисел и строк. Читатели, хорошо знакомые с VB6, могут бегло пролистать эту главу.
Лишь в нескольких
примерах этой главы встречается реальный код, который может использоваться в
серьезных программах VB .NET. Дело в том, что ни одна серьезная программа VB
.NET не обходится без объектов, построенных по шаблонам, называемых классами,
а эта тема будет рассматриваться в главе 4. Мы начнем с базовых конструкций
языка, потому что в противном случае нам пришлось бы ограничиться примитивными
классами, не обладающими никакой практической ценностью. В этой главе не рассматриваются
классы, определяемые пользователем, и продемонстрированы лишь простейшие примеры
использования встроенных классов .NET Framework, обладающих исключительно широкими
возможностями.
Что же из
этого следует? Как ни странно — то, что написанные в этой главе программы весьма
близки по стилю к традиционному программированию ранней эпохи BASIC и даже предшествовавшей
ей эпохи Fortran и COBOL (если не считать некоторых странных, но необходимых
синтаксических конструкций). В частности, в отличие от программ из дальнейших
глав, у них всегда есть четко определенные начало и конец, и управление передается
от начала к концу (хотя управление может передаваться специальными командами).
Как говорилось
во введении, мы постараемся приводить программы, написанные в нормальном стиле
.NET, по возможности избегая искусственной совместимости с VB6.
Каждое приложение
VB .NET должно иметь точку вто§а. В точке входа содержится код,
автоматически выполняемый при запуске, после чего управление передается остальному
коду программы. В относительно простых графических приложениях
точка входа может ассоциироваться с начальной формой, как в VB6. Но как было
показано в главе 1, код форм Windows достаточно сложен и поиск точки входа может
вызвать определенные затруднения. В этой главе рассматриваются только консольные
приложения, работающие в консольном окне (наподобие окна сеанса DOS). Да, VB
.NET позволяет легко создавать традиционные консольные приложения, часто применяемые
при программировании серверных сценариев:
Точкой входа
консольного приложения является процедура Sub Mai n модуля (аналог процедуры
Sub Mai n в VB6). Если выбрать в диалоговом окне New Project значок консольного
приложения (Console Application), VB .NET автоматически генерирует «скелет»
приложения с точкой входа — процедурой Sub Main:
Module Module1
Sub Main()
End Sub
End Module
В отличие
от VB6, в первой строке задается имя модуля (команда выделена жирным шрифтом).
В данном примере используется имя Modul el, принятое по умолчанию. По правилам
имя модуля должно совпадать с именем файла. Допустим, вы изменили имя модуля
в первой строке: Module Testl При попытке запустить консольное приложения выводится
сообщение об ошибке:
Startup code 'Sub Main' was specified in 'Test.Modulel'.
but 'Test.Modulel'
was not found
Переименование
модуля после его создания выполняется следующим образом:
По аналогии
с VB6 программа VB .NET (решение) может состоять из нескольких модулей,
но наличие процедуры Sub Main допускается только в одном модуле. Приложение
завершается по достижении команды End Sub процедуры Sub Mai n. Например, легендарная
программа «Hello world» выглядит следующим образом:
Module Modul
el
Sub Main()
Console.WriteLine("Hello
world")
End Sub End
Module
Если запустить
эту программу в IDE, на экране очень быстро мелькнет (и тут же исчезнет) окно
DOS со словами «Hello world». Окно закрывается по завершении обработки
команды End Sub.
Рис.
3.1. Диалоговое окно свойств консольного приложения
Если включить
в программу строку, выделенную ниже жирным шрифтом, консольное окно остается
на экране до нажатия клавиши Enter (чрезвычайно полезный метод ReadLine() описан
ниже).
Module Modulel
Sub Main()
Console.WriteLine("Hello world")
Console. ReadLine()
End Sub
End Module
Несмотря на простоту, эти две программы демонстрируют одну из ключевых особенностей программирования VB .NET (и вообще программирования на любом объектно-ориентированном языке): вы обращаетесь к объектам с запросом на выполнение операций. По аналогии с VB6 точка («.») используется для обращения к членам объектов и классов. Хотя обычно при вызове указывается объект (конкретный экземпляр, созданный на базе класса), в некоторых случаях вместо него указывается имя класса. В качестве примера возьмем следующую строку:
Console.WriteLine("Hellо
world")
В ней вызывается
метод Wri teLi ne класса Console, предназначенный для вывода текста с последующим
переводом строки (в объектно-ориентированном программировании, как и в VB6,
функции классов обычно называются методами). Метод WriteLine принадлежит
к числу общих (shared) методов, также называемых методами
класса. Общие методы подробно описаны в главе4. При вызове WriteLine выводимый
текст заключаете в кавычки и помещается в круглые скобки. Во вторую версию программы
«Hello world» добавлен вызов метода ReadLi ne, ожидающего нажатия
клавиши Enter (метод ReadLi ne обычно используется в правой части команды присваивания,
чтобы введенный с консоли текст был сохранен в заданной переменной — см. следующее
примечание).
В
приведенных программах следует обратить внимание на пару неочевидных обстоятельств.
Как было сказано выше, при вызове метода обычно указывается конкретный экземпляр
класса. Исключение из этого правила составляют особые методы класса, называемые
общими методами. Общие методы существуют на уровне класса, а не его отдельных
экземпляров. Другая тонкость заключается в том, что Console входит в пространство
имен System, поэтому полный вызов метода выглядит так: System.Console.Writeline("Hello
world"). В данном примере это не нужно; причины изложены в главе 4 при
более подробном описании пространств имен.
Пользователям
предыдущих версий VB следует учесть, что круглые скобки при вызове методов обязательны
— обычно IDE добавляет их автоматически, но лучше не забывать об этом. Ключевое
слово Call разрешено, но теперь в нем нет необходимости.
При вводе
программ VB .NET во внешнем редакторе вы не сможете воспользоваться средствами
IntelliSense. Мы рекомендуем использовать IDE, поскольку технология IntelliSense
значительно упрощает программирование в такой сложной среде, как .NET (конечно,
для этого вам придется перейти от бесплатно распространяемого .NET SDK к Visual
Studio). Редактор IDE даже исправляет некоторые распространенные ошибки — например,
пропуск круглых скобок при вызове некоторых методов.
В
VB .NET, как и во всех предыдущих версиях BASIC, не учитывается регистр символов
(кроме текста, заключенного в кавычки). Пробелы в строках, не заключенных в
кавычки, также игнорируются.
Тем не менее
VS .NET IDE пытается оформлять программы VB .NET по своим правилам. Первые символы
ключевых слов преобразуются к верхнему регистру, а строки дополняются пробелами
для удобства чтения (End SUB преобразуется в End Sub и т. д.). Регистр символов
в именах методов VB .NET определяется по схеме Pascal (слова начинаются
с прописных букв, остальные буквы строчные). Альтернативная схема выбора регистра
(writeLine) для методов VB .NET обычно не используется.
Номера строк
в командах VB .NET практически не используются, хотя строки программы могут
нумероваться, причем каждая команда обычно расположена в отдельной строке. Чтобы
продолжить команду в следующей строке, завершите ее символом подчеркивания (_),
отделенным одним или несколькими пробелами. Таким образом, если строка не завершается
символом подчеркивания, нажатие клавиши Enter является признаком конца команды
(в Visual Basic команды не разманд
можно разместить в одной строке, разделив их символом «:», но обычно
так не поступают. Если введенная строка не помещается в окне, IDE прокручивает
строку вправо по мере необходимости.
Комментарии
в VB .NET, как и в любом языке программирования, необязательны. Они не обрабатываются
компилятором и соответственно не занимают места в откомпилированном коде. В
VB .NET существует два варианта оформления комментариев. В первом, более распространенном
варианте комментарий начинается с апострофа:
Sub Main()
Console.WriteLine("Hello
world")
'
Игнорировать значение, возвращаемое методом ReadLine
Console. ReadLine()
End Sub
Во втором
варианте используется старое ключевое слово Rem, которое появилось в BASIC в
начале 1960-х годов!
При включении
комментариев в конец строки проще воспользоваться апострофом, поскольку ключевое
слово Rem придется отделять двоеточием. В VB .NET не предусмотрено языковых
средств для комментирования нескольких строк, хотя на панели инструментов присутствует
кнопка, упрощающая создание таких комментариев.
В
отличие от языка С#, обладающего встроенными средствами построения комментариев
XML, в VB .NET документация XML будет создаваться отдельной надстройкой (add-in).
Имена переменных в VB .NET имеют длину до 255 символов и обычно начинаются с буквы в кодировке Unicode (за дополнительной информацией о Unicode обращайтесь на сайт www.unicode.org), хотя также допускается символ подчеркивания. Далее следует произвольная комбинация букв, цифр и символов подчеркивания. Все символы имени являются значимыми, но регистр символов игнорируется (как вообще в VB .NET); считается, что firstBase и firstbase — одна и та же переменная. Присваивание выполняется при помощи знака =, как и в более ранних версиях VB:
theYear
= 2001
В
.NET Framework используются новые правила присваивания имен переменным, заметно
изменившиеся по сравнению с VB6. В соответствии рекомендациями, приведенными
в MSDN, применять венгерскую запись нежелательно, а значимые имена переменных
(то есть не состоящие из одной буквы, как i или t) должны оформляться в альтернативной
схеме выбора регистра. В прежних версиях VB обычно использовалась схема Pascal.
Имена переменных не могут совпадать с последовательностями, зарезервированными VB .NET (список для текущей версии приведен в табл. 3.1), однако это ограничение можно обойти, заключив имя переменной в квадратные скобки. Например, переменная не может называться Loop, а имя [Loop] подойдет — хотя делать это не рекомендуется. Зарезервированные слова внутри имен переменных допустимы (скажем, loopit — вполне нормальное имя). При попытке использования ключевого слова в качестве имени переменной VB .NET подчеркивает его и информирует об ошибке (при помощи экранной подсказки).
Таблица
3.1. Ключевые слова текущей версии VB .NET
AddHandler |
AddressOf |
Alias |
And |
Ansi |
||
As |
Assembly |
Auto |
Binary |
BitAnd |
||
BitNot |
BitOr |
BitXor |
Boolean |
ByRef |
||
Byte |
ByVal |
Call |
Case |
Catch |
||
CBool |
CByte |
CChar |
CDate |
CDec |
||
CDbl |
Char |
CInt |
Class |
CLng |
||
CObj |
Compare |
Const |
CShort |
CSng |
||
CStr |
Ctype |
Date |
Decimal |
Declare |
||
Default |
Delegate |
Dim |
Do |
Double |
||
Each |
Else |
Elself |
End |
Enum |
||
Erase |
Error |
Event |
Exit |
Explicit |
||
ExternalSource |
False |
Finally |
For |
Friend |
||
Function |
Get |
GetType |
GoTo |
Handles |
||
If |
Implements |
Imports |
In |
Inherits |
||
Integer |
Interface |
Is |
Lib |
Like |
||
Long |
Loop |
Me |
Mod |
Module |
||
Mustlnherit |
MustOverride |
MyBase |
MyClass |
Namespace |
||
Next |
New |
Not |
Nothing |
Notlnheritable |
||
NotOverridable |
Object |
Off |
On |
Option |
||
Optional |
Or |
Overloads |
Overridable |
Overides |
||
Pa ram Array |
Preserve |
Private |
Property |
Protected |
||
Public |
RaiseEvent |
Readonly |
Re Dim |
REM |
||
RemoveHandler |
Resume |
Return |
Select |
Set |
||
Shadows |
Shared |
Short |
Single |
Static |
||
Step |
Stop |
Strict |
String |
Structure |
||
Sub |
SyncLock |
Text |
Then |
Throw |
||
To |
True |
Try |
TypeOf |
Unicode |
||
Until |
When |
While |
With |
With Events |
||
WriteOnly |
Xor |
|
|
|
||
Литералы
и их соответствие типам данных
Литералом
называется последовательность символов, которая может интерпретироваться
как значение одного из примитивных типов. Но с типами (даже примитивными) в
VB .NET дело обстоит несколько сложнее, чем в более ранних версиях VB.
Хотя возможность
непосредственной интерпретации данных предусмотрена в любом языке программирования,
решить, как именно следует интерпретировать те или иные данные, иногда бывает
непросто. Наверное, все согласятся с тем, что 3 — это число 3 и его следует
интерпретировать именно так. Но что такое число 3 с точки зрения компилятора?
Сколько байт памяти следует под него выделить? Теоретически для хранения числа
3 хватит 2 бит, но в современных языках программирования обычно происходит не
так.
Итак, компилятор
должен проанализировать литерал и принять необходимые решения, поэтому вы должны
по возможности точнее описать, что вы имеете в виду, не полагаясь на разумность
компилятора. Вернемся к примеру с простым числом 3. В VB .NET оно может представлять
собой (среди прочего):
К счастью,
символ 3 никогда не будет автоматически интерпретироваться как строковая
константа (если не переопределять стандартную логику VB). В VB .NET строки и
числа по умолчанию не смешиваются — более подробно эта тема рассматривается
в разделе «Преобразования разнотипных значений» этой главы.
С точки зрения
компилятора простой констатации «это число 3» недостаточно. Разумеется,
VB .NET, как и любой язык программирования, позволяет уточнить смысл литерала.
Например, 31 — литерал типа Integer со значением 3, а литерал "3"
относится к строковому типу String (тип String рассматривается ниже в этой главе;
он несколько отличается от строкового типа в прежних версиях VB).
Примитивные
типы можно рассматривать как атомарные элементы языка, хотя в VB .NET они представляют
собой псевдонимы для классов из библиотеки System.
В переменной,
объявленной с примитивным типом, хранятся значения указанного типа. Ниже перечислены
примитивные числовые типы VB .NET.
Если
суффикс не указан, а число входит в интервал допустимых значений типа Integer,
по умолчанию оно сохраняется в формате Integer. Это связано с тем, что на 32-разрядных
процессорах тип Integer обрабатывается эффективнее остальных типов.
При
объявлении числовых переменных можно использовать старые суффиксы типов %, &
и т. д. — например, литерал 1234% относится к типу Long. Но при этом следует
помнить, что в VB6 и VB .NET эти суффиксы имеют разный смысл, поскольку тип
Integer VB .NET соответствует типу Long V86. По этой причине использовать старый
синтаксис не рекомендуется.
Любой целочисленный
литерал можно записать в шестнадцатеричной системе счисления (по основанию 16),
для чего он снабжается префиксом &Н. Например, литерал &HF соответствует
десятичному числу 15, хранящемуся в формате Integer, поскольку суффикс типа
не указан, а число входит в интервал допустимых значений типа Integer. Числа,
записанные в восьмеричной системе счисления (по основанию 8), снабжаются префиксом
&0.
При выполнении
операций с вещественными числами используются следующие типы:
Новый тип
Decimal пришел на смену старому типу Currency, использовавшемуся в прежних версиях
VB. Он используется в ситуациях, когда ошибки округления недопустимы.
Применение
суффикса типа в литералах помогает избежать путаницы и случайных ошибок переполнения,
возникающих при умножении двух чисел. При выполнении следующей команды:
Console.WriteLine(12345678
* 4567)
компилятор
выдает ошибку:
This constant expression produces a value that is not representable in type System.Integer.
Проблема решается при помощи суффикса типа Long:
Console.WriteLine(123456781
* 4567)
Общие методы
MaxValue и MinValue, ассоциированные с типом, возвращают соответственно верхнюю
и нижнюю границы интервала допустимых значений. Пример:
Console.WriteLine(Integer.MaxValue)
В табл. 3.2
собраны данные о соответствии числовых типов VB .NET, типов .NET Framework и
их аналогов из VB6 (если они есть).
Таблица
3.2. Соответствие между числовыми типами
Тип VB. NET |
Тип .NET
Framework |
Тип VB6 |
||
Byte |
System. Byte |
Byte |
||
Boolean |
System. Boolean |
Boolean |
||
Decimal |
System. Decimal |
— |
||
— |
— |
Currency |
||
Double |
System. Double |
Double |
||
Short |
System. Intl6 |
Integer |
||
Integer |
System.Int32 |
Long |
||
Long |
System.Int64 |
— |
||
Single |
System. Single |
Single |
||
Кроме числовых
литералов также существуют литералы типов Boolean, Date и Char. Тип данных Bool
ean принимает значения True и Fal se. В VB .NET он представляется 4 байтами
(в отличие от 2 байт в VB6).
В
VB .NET бета-версии 1 значение True было равно +1 (как и в других языках .NET).
Начиная с бета-версии 2 оно снова стало равно -1. Говоря точнее, в поразрядных
операциях и при преобразовании к числовым типам значение True равно -1, а не
1. Но если логическая величина VB .NET передается за пределы VB, при приведении
к числовому типу нового языка она считается равной 1. Пожалуй, это решение было
ошибочным, поскольку одной из целей, поставленных при разработке .NET, было
обеспечение максимальной межъязыковой совместимости. Пока вы ограничиваетесь
встроенными константами True и False, все будет нормально, но стоит перейти
к конкретным числовым значениям — и у вас могут возникнуть проблемы.
Тип данных
Date представляет дату и/или время. Как ИГ в VB5, такие литералы заключаются
между символами #...# — например, #Jan 1. 2001#. Если время не указано), предполагается,
что литерал соответствует полуночи указанной даты.
Тип
Date в VB .NET не преобразуется к типу Double. В частности, из этого следует,
что с датами нельзя производить математические вычисления — например, вычислить
завтрашнюю дату командой Today+1.
Тип данных
Char представляет один символ Unicode. Объем кодировки Unicode (65 536 символов)
вполне достаточен для представления всех существующих алфавитов. Обычно символ
заключается в кавычки, за которыми следует префикс С (например, "Н"С),
но вы также можете воспользоваться встроенной функцией Chr и указать числовой
код символа Unicode. Например, запись Chr(&H2153) представляет символ 1/3
кодировке Unicode, хотя в некоторых операционных системах этот символ не будет
отображаться во время работы программы. Если заключить один символ в кавычки
без суффикса «С», вы получите тип Stri ng вместо Char, а автоматическое
преобразование между этими типами не поддерживается (команда Opti on Strict
описана ниже в этой главе).
В VB .NET,
как и в VB6, переменные объявляются в процедурах и функциях при помощи ключевых
слов Dim и As, а присваивание выполняется знаком =:
Dim foo As String
foo = "bar"
Если вы не
изменяли стандартную настройку VB .NET, переменные должны объявляться перед
использованием (режим Option Explicit, впервые представленный в VB4, теперь
используется по умолчанию). В VB .NET поддерживается инициализация переменных
при объявлении. Пример:
Dim salesTax
As Decimal = 0.0825D
Команда объявляет
переменную с именем salesTax и присваивает ей начальное значение 0.0825 типа
Decimal. При инициализации могут использоваться любые синтаксически правильные
выражения VB .NET. Следующая команда присваивает переменной startAngle встроенное
значение математической константы п, используя для этого константу класса
System. Math: Dim startAngle As Decimal - Math.PI
Если переменная
не была инициализирована при объявлении, ей присваивается стандартное значение,
соответствующее ее типу, — например, числовым переменным присваивается 0. При
таком удобном синтаксисе, как в VB .NET, всегда лучше инициализировать переменную
при объявлении, чем полагаться на значение по умолчанию. В следующем фрагменте
используется оператор &, применявшийся в VB6 для конкатенации строк:
Sub Main()
Dim salesTax
As Decimal = 0.0825D
Dim state As
String = "California"
Console.WriteLine("The
sales tax in " & state & " is " & salesTax)
Console. ReadLine()
End Sub
Программа
выводит следующий результат:
The sales tax
in California is 0.0825
В отличие от предыдущих версий VB, объявление нескольких переменных в одной строке программы работает именно так, как подсказывает здравый смысл. Иначе говоря, следующая команда объявляет три переменные: i, j и k, относящиеся к типу Integer:
Dim i, j, k As
Integer
При одновременном
объявлении нескольких переменных инициализация выполняться не может, поэтому
следующая строка недопустима:
Dim i, j, k
As Integer = 1
Как и в прежних
версиях VB, вместо указания типа с ключевым словом As может использоваться суффикс
типа. Например:
Dim i%, myname$
Приведенная
команда объявляет переменную i типа Integer (аналог Long в старом VB) и переменную
myName типа String. Программистам VB .NET поступать подобным образом не рекомендуется.
Все программисты
хорошо помнят, какие жаркие дискуссии проходили по поводу соглашений об именах
переменных. Существует сложная система префиксов (так называемая венгерская
запись), позволяющая с первого взгляда определить тип переменных. Согласно общим
рекомендациям программирования для .NET Framework применение венгерской записи
нежелательно. Мы будем следовать этим рекомендациям, и в книге префиксы встречаются
очень редко.
Команды
семейства DefType (например, Deflnt) в VB .NET не поддерживаются.
Преобразования
разнотипных значений
По мнению
многих программистов, прежние версии VB6 слишком либерально относились к преобразованию
типов. В результате возникало явление «злостного искажения типов»
— скажем, VB6 позволял умножить строковое представление числа на Integer.
В VB .NET
предусмотрен режим жесткой проверки типов Option Strict. Чтобы активизировать
его, включите следующую команду в начало программы (также можно воспользоваться
флажком Strict Type Checking на вкладке Build диалогового окна Project Properties):
Option Strict
On
При активизации
этого режима (а это следует делать всегда!) VB .NET требует, чтобы любые
преобразования типов, которые могут привести к потере данных, выполнялись явно.
Например, при преобразовании Single к типу Integer может произойти потеря точности,
поскольку тип Integer не позволяет представить весь интервал допустимых значений
типа Single. С другой стороны, если потеря данных исключена (скажем, при преобразовании
Integer в тип Long или Decimal), VB .NET выполняет преобразование автоматически.
В документации VB .NET преобразования
без потери данных называются расширяющими преобразованиями (widening
conversions). В табл. 3.3 переделены допустимые расширяющие преобразования для
базовых типов данных.
Таблица
3.3. Допустимые расширяющие преобразования для базовых типов VB .NET
Тип |
Допустимое
расширение |
||
Byte | Byte, Short, Integer, Long, Decimal Single, Double | ||
Short | Short, Integer, Long, Decimal, Single, Double | ||
Integer | Integer, Long, DecimaL Single, Double | ||
Long | Long, DecimaL Single, Double | ||
Single | Single, Double | ||
Date |
Date, String |
||
Более того,
при активном режиме жесткой проверки типов вы не сможете использовать
конструкции вида:
Dim foo As Boolean
foo = 3
В этом фрагменте
логической переменной foo значение True присваивается в виде ненулевого числа
(в VB6 это было вполне распространенным явлением). Подобные преобразования должны
выполняться явно:
Dim foo As Boolean
foo =СВооl(З)
VB .NET автоматически
выполняет преобразование между символьным типом и строкой, состоящей из одного
символа.
При желании
вы можете вернуться к доисторическим временам и отказаться от жесткой проверки
типов. Для этого достаточно начать модуль со следующей команды:
Option Strict Off
Впрочем, поступать
подобным образом не рекомендуется.
Если содержимое
переменной одного типа может быть преобразовано к другому типу, можно воспользоваться
функцией явного преобразования, как в только что приведенном примере с СВооl.
Функции явного преобразования типов перечислены в табл. 3.4.
Таблица
3.4. Функции явного преобразования типов
Функция |
Описание |
||
CBool | Преобразует выражение к типу Boolean | ||
CByte | Преобразует выражение к типу Byte | ||
CInt | Преобразует выражение к типу Integer с округлением | ||
CIng | Преобразует выражение к типу Long с округлением | ||
CSng | Преобразует выражение к типу Single | ||
CDate |
Преобразует выражение к типу Date |
||
Функция |
Описание |
||
СDbl | Преобразует выражение к типу Double | ||
CDec | Преобразует выражение к типу Decimal | ||
CStr | Преобразует выражение к типу String | ||
CChar |
Преобразует первый символ строки к типу Char |
||
,VB .NET
выполняет числовые преобразования только в том случае, если преобразуемое число
входит в интервал допустимых значений нового типа; в противном случае выдается
сообщение об ошибке.
На
первый взгляд кажется, что тип Char можно интерпретировать как короткое целое
без знака (то есть целое в интервале от 0 до 65 535), но делать этого не следует.
Начиная с бета-версии 2 было запрещено преобразование Char в число функциями
семейства CInt; вместо этого используется встроенная функция Asc.
Тип object и исчезновение типа Variant
Вероятно,
вы заметили, что при описании типов нигде не упоминается тип Variant. BVB .NET
этот тип не поддерживается — и это очень хорошо! В VB6 переменные Variant допускали
хранение данных произвольного типа. Программисты часто злоупотребляли этой возможностью,
что приводило к возникновению нетривиальных ошибок в программах. В VB .NET все
типы данных (даже числовые, как Integer) являются частными случаями типа Object.
Может показаться, что тип Object стал аналогом Variant в VB .NET, но это не
так. Как будет показано в главах 4 и 5, тип Object занимает в программировании
.NET значительно более важное место и обладает множеством интересных возможностей.
Мы вернемся к типу Object в главах 4 и 5.
Ниже приведен
хрестоматийный пример — преобразование температуры по Цельсию в температуру
по шкале Фаренгейта. Мы руководствуемся следующими предположениями:
' Преобразование температуры по Цельсию в температуру по Фаренгейту
Option Strict On Module
Modulel Sub
Main()
Dim cdeg As
Decimal
Console. Writer
Enter the degrees in centigrade...")
cdeg = CDec(Console.ReadLine())
Dim fdeg As
Decimal
fdeg = (((9@
/ 5) * cdeg) + 32)
Console.WriteLine(cdeg
& " is " & fdeg & " degrees Fahrenheit.")
Console. ReadLine()
End Sub
End Module
Обратите
внимание на суффикс @ — он гарантирует, что при вычислениях будет использоваться
тип Decimal. Если убрать этот суффикс, то при включенной жесткой проверке типов
будет выдано сообщение об ошибке!
При использовании
простого оператора / для деления в VB .NET необходимо учитывать некоторые нюансы.
За дополнительной информацией обращайтесь к разделу «Математические операторы».
Строковая
переменная содержит текст в кодировке Unicode длиной до 231 (более
2 миллиардов!) символов. Как было показано выше, значения строкового типа заключаются
в кавычки:
Dim message As String
message = "Help"
Конкатенация
(объединение) строк обычно выполняется оператором &. Старый вариант с оператором
+ тоже работает, но при отключении жесткой проверки типов он может вызвать серьезные
проблемы, поэтому использовать знак + при конкатенации не рекомендуется. Старый
способ идентификации строковых переменных с суффиксом $ (например, aStringVariableS)
иногда используется для временных переменных.
В
VB .NET строковые переменные не относятся к базовому типу, а являются экземпляра-ми
класса String. Некоторые нюансы, связанные с их применением, будут рассмотрены
в главе 4, а пока мы упомянем лишь одну особенность, о которой необходимо знать
для эффективной работы со строками в VB .NET: при любой модификации строки в
VB .NET создается новый экземпляр строки. Частая модификация строки требует
больших затрат ресурсов, поэтому в VB .NET имеется класс StringBuilder для выполнения
подобных операций (например, выборки данных из буфера и объединения их в строковой
переменной).
В
отличие от предыдущих версий VB, в VB .NET не поддерживаются строки фиксированной
длины.
В вашем распоряжении
остались все классические строковые функции VB6 (Left, Right, Mid и т. д.),
но версии этих функций с суффиксом $ теперь не поддерживаются. В табл. 3.5 перечислены
важнейшие функции класса String, заменяющие строковые функции VB6. Не забывайте,
что при многократной модификации строк (например, при вызове Mid в цикле) следует
использовать класс StringBuilder, описанный в главе 4. Некоторые из приведенных
методов используют массивы, которые будут рассмотрены ниже в этой главе.
При программировании
для .NET рекомендуется по возможности использовать методы и свойства класса
Stri ng, входящего в .NET Framework. Самые распространенные строковые методы
перечислены в табл. 3.6.
Таблица
3.5. Основные строковые функции
Функция |
Описание |
||
Asc |
Возвращает код
первого символа в строке |
||
Chr |
Преобразует
число в символ Unicode |
||
Filter |
Получает строковый
массив и искомую строку; возвращает одномерный массив всех элементов,
в которых был найден заданный текст |
||
GetChar |
Возвращает символ
строки с заданным индексом в формате Char. Индексация символов начинается
с 1. Например, команда GetChar("Hello",2) возвращает символ
«е» в виде типа Char |
||
InStr |
Возвращает позицию
первого вхождения одной строки в другой строке |
||
InStrRev |
Возвращает позицию
последнего вхождения одной строки в другой строке |
||
Join |
Строит большую
строку из меньших строк |
||
LCase |
Преобразует
строку к нижнему регистру |
||
Left |
Находит или
удаляет заданное количество символов от начала строки |
||
Len |
Возвращает длину
строки |
||
LTrim |
Удаляет пробелы
в начале строки |
||
Mid |
Находит или
удаляет символы в строке |
||
Replace |
Заменяет одно
или более вхождений одной строки в другой строке |
||
Right |
Находит или
удаляет заданное количество символов в конце строки |
||
RTrim |
Удаляет пробелы
в конце строки |
||
Space |
Генерирует строку
заданной длины, состоящую из пробелов |
||
Split |
Позволяет разбивать
строку по заданным разделителям (например, пробелам) |
||
Str |
Возвращает строковое
представление числа |
||
StrComp |
Альтернативный
способ сравнения строк |
||
StrConv |
Преобразует
строку из одной формы в другую (например, с изменением регистра) |
||
String |
Создает строку,
состоящую из многократно повторяющегося символа |
||
Trim |
Удаляет пробелы
в начале и конце строки |
||
UCase |
Преобразует
строку к верхнему регистру |
||
Таблица
З.6. Основные строковые методы и свойства .NET Framework
Метод/свойство |
Описание |
|||
Chars | Возвращает символ, находящийся в заданной позиции строки | |||
Compare | Сравнивает две строки | |||
Copy | Копирует существующую строку | |||
Copy To | Копирует заданное количество символов, начиная в заданную позицию массива символов | |||
Empty | Константа, представляющая пустую строку | |||
EndsWith | Проверяет, завершается ли заданная строка определенной последовательностью символов | |||
IndexOf | Возвращает индекс первого вхождения подстроки в заданной строке | |||
Метод/свойство |
Описание |
||
Insert |
Возвращает новую строку, полученную вставкой подстроки в заданную позицию |
||
Join |
Объединяет массив
строк с заданным разделителем |
||
LastlndexOf |
Возвращает индекс последнего вхождения заданного символа или подстроки в строке |
||
Length |
Возвращает количество
символов в строке |
||
PadLeft |
Выравнивает
символы строки по правому краю. Строка дополняется слева пробелами
или другими символами до заданной длины |
||
PadRight |
Выравнивает
символы строки по левому краю. Строка дополняется справа пробелами
или другими символами до заданной длины |
||
Remove |
Удаляет из строки
заданное количество символов, начиная с заданной позиции |
||
Replace |
Заменяет все
вхождения подстроки другой подстрокой |
||
Split |
Разбивает строку,
превращая ее в массив подстрок |
||
Starts With
|
Проверяет, начинается ли заданная строка определенной последовательностью символов |
||
Substring |
Возвращает подстроку,
начинающуюся с заданной позиции |
||
ToCharArray |
Копирует символы
строки в символьный массив |
||
ToLower |
Возвращает копию
строки, преобразованную к нижнему регистру |
||
ToUpper |
Возвращает копию
строки, преобразованную к верхнему регистру |
||
Trim |
Удаляет пробелы
или все символы из набора, заданного в виде массива символов Unicode,
в начале и конце строки |
||
TrimEnd |
Удаляет пробелы
или все символы из набора, заданного в виде массива символов Unicode,
в конце строки |
||
TrimStart |
Удаляет пробелы
или все символы из набора, заданного в виде массива символов Unicode,
в начале строки |
||
В
отличие от VB6, где индексация символов в строке начиналась с 1, в методах .NET
Framework индекс первого символа равен 0.
Поскольку
в .NET Framework строки являются объектам, синтаксис вызова этих методов достаточно
логичен и удобен. Выполните следующую программу:
Sub Main()
Dim river As String =" Mississippi Missippi"
'один пробел
слева
Consol e. Wri
teLi ne( ri ver. Tollpper ())
Console.Wri
teLi net ri ver.ToLower())
Console.WriteLineCriver.Trim())
Console. WriteLinetri
ver. EndsWith("I"))
Consol e.Wri
teLi ne С ri ver.EndsWith("i"))
Console.WriteLine(river.IndexOf("s"))
'Индексация начинается
с 0!
Console.WriteLineCriver.Insert(9. " river"))
'Индексация
' начинается
с 0!
Consol e.ReadLine()
End Sub
Результат выглядит
так:
MISSISSIPPI
MISSIPPI
mississippi
missippi
Mississippi
Missippi
False
True
3
Mississi riverppi
Missippi
Все функции
форматирования возвращают новую строку в заданном формате. В VB .NET сохранены
аналоги старых функций форматирования из VB6 и VBScript, поэтому вы можете продолжать
использовать функции Format, Format Number, For-matCurrency, FormatPercent и
FormatDateTime. Последние четыре функции неплохо справляются с простым форматированием,
но мы все равно предпочитаем использовать более мощные средства форматирования,
реализованные в .NET Framework.
Синтаксис
форматирования в .NET Framework на первый взгляд выглядит несколько странно.
Рассмотрим несложный пример:
Dim balance
As Decimal = 123456
Dim creditLimit
As Decimal = 999999
Console.WriteLine("Customer
balance is {0:C}, credit limit is {1:C} ",_
balance. creditLimit
= balance)
Результат:
Customer balance
is $123,456.00. credit limit is $876.543.00
Попробуйте
привести фрагмент, выделенный жирным шрифтом, к следующему виду:
Console.WriteLine("Customer credit is {1:C}, balance is {0:C} ".
balance. creditLimit
= balance)
Вы получите
следующий результат:
Customer credit
is $876.543.00. balance is $123.456.00
Форматируемые
переменные перечисляются в порядке их следования в списке. Так, во втором примере
{1:С} означает вторую переменную в списке, а {0:С} соответствует первой переменной
(напомним, что индексация в .NET Framework начинается с 0). «С»
означает форматирование в денежном формате, определенном в параметрах локального
контекста Windows.
В табл. 3.7
приведены условные обозначения шести базовых математических операций.
Результат
стандартного деления (/) всегда относится к типу Double, даже в случае де-ления
без остатка. Результат целочисленного деления (\) всегда относится к типу Integer.
Это означает, что при работе с типами Decimal и Integer вам придется часто использовать
функции преобразования.
Таблица
3.7. Математические операции
Оператор |
Операция |
||
+ | Сложение | ||
- | Вычитание (и обозначение отрицательных чисел) | ||
/ | Деление (преобразование к Double — не может вызвать исключение DivideByZero; см. главу 7) | ||
\ | Целочисленное деление (без преобразования — может вызвать исключение DivideByZero) | ||
* | Умножение | ||
^ | Возведение в степень | ||
Чтобы лучше
разобраться в разных типах деления, можно воспользоваться методом .NET GetType.
В командах вывода (таких как WriteLine) этот метод возвращает имя типа в строковом
представлении. Рассмотрим следующую программу:
Module Modulel
Sub Main()
Console.WriteLine((4
/ 2).GetType())
Console. ReadLine()
End Sub
End Module
В консольном
окне выводится строка
System.Double
Возможности
метода GetType не ограничиваются простым выводом имени — в частности, он используется
в процессе рефлексии. Механизм рефлексии описан в главе 4.
Ниже приведен
пример ситуации, в которой необходимо учитывать тип значения, возвращаемого
оператором деления. Перед нами простая (но нерабочая) версия программы, преобразующей
температуру по Цельсию в температуру по Фаренгейту. В выделенной строке отсутствует
суффикс @, преобразующий результат деления к типу Decimal:
Option Strict On
Module Modulel
Sub Main()
Dim cdeg As
Decimal
Console.. Writer
Enter the degrees in centigrade...")
cdeg=CDec(Console.ReadLine())
Dim fdeg As
Decimal
fdeg = (((9
/ 5) * cdeg) + 32)
Console.WriteLine(cdeg
& " is " & fdeg & " degrees Fahrenheit.")
Console. ReadLine()
End Sub
End Module
Из-за присутствия
знака / в выделенной строке переменной fdeg присваивается результат типа Double.
В режиме жесткой проверки типов это приводит к тому, что на стадии компиляции
будет выдано следующее сообщение об ошибке:
Option Strict
disallows implicit conversions from Double to Decimal.
Как исправить ошибку? Только не надо убирать команду Option Strict — это одно из лучших новшеств VB .NET, которое избавляет вас от злостного искажения типов. Лучше воспользуйтесь суффиксом @ или преобразуйте выражение (полностью или частично) к типу Decimal. Пример:
fdeg = ((CDec(9
/ 5) * cdeg) + 32)
Поскольку
результат деления преобразуется к типу Decimal, результат тоже относится к типу
Decimal.
Остается
лишь заметить, что в этом простом примере мы используем метод Write вместо Wri
teLi ne, чтобы предотвратить перевод строки после вывода текста. Кроме того,
в реальной программе введенные данные следовало бы предварительно проанализировать,
потому что пользователи часто ошибаются при вводе.
Наконец,
вещественное деление в VB .NET соответствует стандарту IEEE, поэтому вместо
ошибки деления на ноль теперь происходит нечто странное. Пример:
Sub Main()
Dim getData
As String
Dim x, у As
Double
x = 4
У = 0
Console.WriteLine("What
is 4/0 in VB .NET? " & x / y)
Console.ReadLine()
End Sub
Результат
выглядит так:
What is 4/0
in VB. NET? Infinity
Результат деления 0/0 равен
NaN (Not A Number,
«не является числом»).
В табл. 3.8
перечислены операторы, используемые только при делении чисел типа Integer и
Long.
Таблица
3.8. Математические операторы целочисленного деления
Оператор |
Операция |
||
\ |
Целочисленное
деление любых целых чисел |
||
Mod |
Остаток от
целочисленного деления |
||
Оператор
\ игнорирует остаток от деления и возвращает результат типа Integer (если он
относится к интервалу допустимых значений этого типа). Например, 7\3=21. Напомним,
что оператор / дает результат типа Double; если вы хотите, чтобы частное относилось
к типу Integer — воспользуйтесь оператором \ или функцией преобразования типа.
Оператор
Mod дополняет оператор целочисленного деления и возвращает остаток от целочисленного
деления. Например, 7 Mod 3 = 1. Если целые числа делятся без остатка, оператор
Mod возвращает 0: 8 Mod 4 = 0.
Круглые
скобки и приоритет операций
При обработке
сложных выражений последовательность выполнения операций задается двумя способами.
При использовании круглых Скобок вам не придется запоминать приоритеты различных
операций. В VB .NET, как и во многих языках программирования, операции обладают
приоритетом, определяющим последовательность их выполнения. Умножение обладает
более высоким приоритетом, чем сложение; следовательно, выражение 3+4*5 равно
23, поскольку умножение (4*5) выполняется раньше, чем сложение.
Ниже перечислены
математические операции в порядке убывания приоритета.
Если две
операции обладают одинаковым приоритетом, порядок выполнения определяется порядком
их следования в выражении (слева направо).
Сокращенная
запись операций с присваиванием
Для некоторых
операций, объединенных с присваиванием, в VB. NET предусмотрены сокращенные
обозначения, перечисленные в следующей таблице.
Сокращенная
запись |
Эквивалент |
||
А*=В |
А = А*В |
||
А+=В |
А = А + В |
||
А/=В |
А = А/В |
||
А-=В |
А = А-В |
||
А\=В |
А = А\В |
||
А^=В |
А = А^В |
||
А&=В |
А = А &
В (конкатенация строк) |
||
Математические
функции и математические константы
Встроенные
математические функции VB6 работают и в VB .NET, но мы предпочитаем использовать
методы класса Math, входящего в .NET Framework. В этот класс также входят некоторые
полезные константы (например, Math. PI и Math. Е). Основные математические функции
класса Math перечислены в табл. 3.9. Все эти функции объявлены общими (shared),
поэтому они принадлежат классу Math в целом, а не его отдельным экземплярам
(которые, впрочем, все равно невозможно создать — см. главу 4).
Все перечисленные
методы являются общими методами класса Math, поэтому они должны вызываться с
префиксом Math — например, Math.Log10(l0).
В
VB .NET предусмотрена целая группа методов для получения разнообразных случай-ных
чисел. Мы рассмотрим эти методы в главе 4, когда речь пойдет о создании объектов.
Таблица
3.9. Общие математические функции класса Math
Математическая
функция |
Описание |
||
Abs |
Возвращает абсолютное
значение (модуль) числа |
||
Acos |
Возвращает угол,
косинус которого равен заданному числу |
||
Asin |
Возвращает угол,
синус которого равен заданному числу |
||
Atan |
Возвращает угол,
тангенс которого равен заданному числу |
||
Ceiling
|
Возвращает наименьшее целое число, большее либо равное заданному числу |
||
Cos |
Возвращает косинус
заданного угла |
||
Exp |
Возвращает число е (приблизительно 2,71828182845905), возведенное в заданную степень |
||
Floor |
Возвращает наибольшее целое число, большее либо равное заданному числу |
||
Log |
Возвращает натуральный
логарифм |
||
Log10 |
Возвращает десятичный
логарифм |
||
Max |
Возвращает большее
из двух заданных чисел |
||
Min |
Возвращает меньшее
из двух заданных чисел |
||
Round |
Возвращает целое
число, ближайшее к заданному числу |
||
Sign |
Возвращает величину,
определяющую знак числа |
||
- Sin |
Возвращает синус
заданного угла |
||
Sqrt |
Возвращает квадратный
корень |
||
Tan |
Возвращает тангенс
заданного угла |
||
VB .NET позволяет
создавать именованные константы для значений, остающихся постоянными на протяжении
всей работы программы. Константы объявляются по аналогии с переменными, а их
имена подчиняются тем же правилам: до 255 символов, начинаются с буквы, после
которой следует произвольная комбинация букв, цифр и символов подчеркивания.
В книге имена констант записываются прописными буквами.
В VB .NET
при активизации жесткой проверки типов необходимо явно указывать тип констант:
Const PIE = 3.14159 ' Не будет компилироваться с Option Strict
Const PIE As
Double = 3.14159 ' Правильно, но Math.PI лучше :-)
Значение
констант может определяться числовыми выражениями, в том числе и содержащими
ранее определенные константы:
Const PIE_OVER_2
As Double = PIE / 2
Аналогичным
образом определяются строковые константы:
Const USER_NAME
As String = "Bill Gates"
.NET Framework
содержит немило встроенных, заранее определенных глобальных констант, которые
вы можете использовать в своих программах. Многие из них аналогичны константам
VB6 с префиксом vb, но они являются членами различных классов, поэтому обращения
к ним выглядят несколько иначе. Например, константа vbCrLf в VB .NET принадлежит
классу ControlChars, поэтому при обращении к ней используется запись Control
Chars. CrLf.
В VB .NET,
как практически во всех языках программирования, существуют циклы — конструкции,
позволяющие выполнять операции заданное количество раз или продолжать, пока
выполняется (или наоборот, не выполняется) некоторое логическое условие. По
сравнению с прежними версиями VB синтаксис циклов мало изменился. В частности,
изменилась конструкция While/Wend, но это изменение к лучшему.
Цикл, выполняемый
заданное количество раз, определяется при помощи ключевых слов For и Next. Например,
следующая программа выводит в консольном окне числа от 1 до 10:
Sub Main()
Dim i As Integer
For i = 1 To
10
Console.WriteLine(i)
Next 1
Console.ReadLine()
End Sub
Обычно переменной-счетчику
присваивается начальное значение, после чего проверяется, не превышает ли текущее
значение счетчика конечное. Если счетчик превысил конечное значение, тело цикла
не выполняется. Если текущее значение меньше конечного, VB .NET выполняет последующие
команды до тех пор, пока не встретит ключевое слово Next (указывать имя переменной
в команде Next необязательно). По умолчанию счетчик увеличивается на 1, и все
начинается заново. Процесс продолжается до тех пор, пока при очередной проверке
не окажется, что счетчик превысил конечное значение. В этот момент цикл завершается,
и управление передается следующей за ним команде.
Хотя
в качестве счетчика может использоваться числовая переменная любого типа, ре-комендуется
использовать переменные типа Integer. В этом случае VB .NET тратит минимальное
количество времени на изменение счетчика, что ускоряет выполнение цикла.
Единичное
приращение счетчика, используемое по умолчанию, иногда неудобно—в некоторых
ситуациях счетчик должен изменяться на 2, на дробную величину или в обратном
направлении. Как и во всех прежних версиях VB, нестандартное приращение указывается
в цикле For-Next с ключевым словом Step.
Следующая
программа имитирует обратный отсчет перед запуском космического корабля:
Sub Main()
Dim i As Integer
For i = 10 To
1 Step =1
Console.WriteLine("It's
t minus " & i & " and counting.")
Next i
Console.WriteLine("Blastoff!")
Console. ReadLine()
End Sub
При отрицательном
приращении тело цикла For-Next игнорируется в том случае, если начальное значение
счетчика меньше конечного. Это очень удобно при выполнении таких операций, как
удаление элементов из списка. Если бы отсчет велся от 0 до ListCount, то на
середине произошло бы обращение к удаленному элементу, тогда как при отсчете
от ListCount до 0 с шагом -1 элементы нормально удаляются от последнего к первому.
Значение Step может относиться к любому числовому типу. Пример:
for yearlylnterest
= .07 То .09 Step .00125D
Цикл перебирает
значения от 7 до 9 процентов с приращением в 1/8 процента. Обратите внимание
на использование типа Decimal для предотвращения ошибок округления.
VB .NET,
как и прежние версии VB, позволяет создавать вложенные циклы практически неограниченной
глубины. Следующий фрагмент выводит таблицу умножения с простейшим форматированием:
Sub Main()
Dim i, j As Integer
For j = 2 To
12
For i = 2 To
12
Console.Writed * j & " ")
Next i
Console. WriteLine()
Next j
Console ReadLine()
End Sub
Во внутреннем
цикле вместо метода WriteLine используется метод Write, чтобы избежать перевода
строки при выводе соседних элементов.
Циклы
с неопределенным условием
Довольно
часто условие продолжения цикла зависит от результатов, полученных в теле цикла.
Следующая конструкция используется в VB .NET для построения цикла с неопределенным
условием, тело которого выполняется минимум один раз (завершающая проверка):
Do
' Команды VB
.NET (0 и более)
Until условие_выполняется
Конечно,
условие не ограничивается простой проверкой равенства. В вашем распоряжении
операторы сравнения, перечисленные в табл.3.10.
Таблица
3.10. Операторы сравнения
Символ |
Проверяемое
условие |
||
<> |
Не равно |
||
< |
Меньше |
||
<= |
Меньше или равно |
||
> |
Больше |
||
>= |
Больше или равно |
||
Строковые
операнды по умолчанию сравниваются в соответствии с порядком символов Unicode.
Таким образом, «А» предшествует «В», но «В»
предшествует «а» (а пробел предшествует любому печатному символу).
Строка «aBCD» предшествует строке «CDE» (то есть считается
«меньше» ее), поскольку прописные буквы в кодировке стоят раньше
строчных.
Как
и в VB6, вы можете игнорировать регистр символов во всех сравнениях модуля или
формы; для этого в начало модуля или формы включается команда Option Compare
Text. Команда Option Compare Binary возвращается к стандартному сравнению строк
в соответствии с положением символов в кодировке ANSI. При активном режиме Option
Compare Text используется порядок символов для страны, указанной при установке
системы Windows.
Ключевое
слово Unti 1 можно заменить ключевым словом Whi I e (при этом следует заменить
условие на противоположное). Например, фрагмент
Do
' Команды VB .NET (0 и более)
Loop Until X
<> String.Empty
эквивалентен
следующему фрагменту:
Do
' Команды VB .NET (0 и более)
Loop While X
= String.Empty
Обратите
внимание на использование константы String.Empty вместо пустой строки "",
менее наглядной и чаще приводящей к ошибкам. Если переместить ключевое слово
Whi 1е или Unti 1 в секцию Do, проверка будет выполняться в начале цикла (и
при ложном условии цикл не будет выполнен ни одного раза). Пример:
Do While Text1.Text <> String.Empty
' Обработать
непустой текст Loop
Условия объединяются при помощи операторов Or, Not и And. Пример:
Do While count
< 20 And savings < 1000000
Если
вы предпочитаете использовать старую конструкцию While-Wend, учтите, что клю-чевое
слово Wend было заменено командой End While.
Условные
команды и принятие решений
" В
VB .NET условная команда If, как и в VB6, существует в двух версиях — однострочной
и многострочной:
If X <
0 Then Console.WriteLine("Number must be positive!")
Условие конструкции
If-Then может содержать логические операторы And, Or и Not. Довольно часто программа
выполняет разные действия в зависимости от того, окажется ли условие истинным
или ложным. В этом случае базовая форма команды
If-Then:
If условие Then
' Команды VB
.NET (0 и более) End If
дополняется
одной или несколькими секциями El se:
If условие Then
' Команды VB
.NET (0 и более) Else
' Команды VB .NET (0 и более)
End If
Несколько
последовательных проверок в секциях Else можно оформить в виде конструкции Elself:
If условие Then
' Команды
Elself условие
Then
' Команды
Elself условие
Then
' Команды
Else
' Команды
End If
Конструкция
If-Then может использоваться для преждевременного выхода из цикла — для этого
она объединяется с командой Exit Do или Exit For. Встретив команду Exit Do или
Exit For, VB .NET немедленно завершает цикл и продолжает выполнение программы
с команды, следующей за ключевым словом Loop или Next (в зависимости от типа
цикла).
Изменения
в видимости переменных
Область видимости
переменных и методов в VB .NET определяется по более сложным правилам, чем в
прежних версиях VB. Эта тема подробно описана в главах 4 и 5. В частности, изменения
проявляются при объявлении переменных в теле цикла или блока If-Then. Такие
переменные невидимы за пределами блока, в котором они были объявлены.
Например, в следующем фрагменте мы выбираем одну из двух версий строковой переменной
Ri sk и затем пытаемся использовать ее:
If income <
100000 Then
Dim risk As
String = "too much risk" Else
Dim risk As
String = "love to make a deal"
End If
Console.WriteLine("Your
risk level is " & Risk)
На экране появляется
сообщение об ошибке:
The name 'risk'
is not declared.
Видимость
обеих версий переменной risk ограничивается блоком, в котором они были
объявлены! Мораль: не объявляйте переменные внутри блоков, если для этого нет
веских причин.
Если компилятор обнаруживает, что проверенная часть сложного логического условия однозначно определяет результат, он не проверяет остаток выражения. Это называется ускоренной проверкой (short curcuiting). Например, если в следующем примере переменная foo ложна, компилятор не проверяет переменную bar:
If foo And bar
Then...
Так было
в VB .NET бета-версии 1, но в прежних версиях VB ускоренная проверка не применялась.
После многочисленных жалоб разработчики Microsoft вернули старую интерпретацию
логических операторов And и Or и добавили новые ключевые слова AndAlso и OrElse,
поддерживающие ускоренную проверку:
If foo AndAlso
Then...
В качестве
альтернативы для громоздких конструкций с множеством Elself в VB .NET была сохранена
команда Select Case, упрощающая принятие решений в зависимости от состояния
числовой или строковой переменной. Пример:
Select Case average
Case Is >
90
Console.WriteLine("A")
Case Is >
80
Console. Wri teLi ne("B")
Case Is >
70
Console.WriteLine("C")
Case Else
Console.WriteLine("You fail")
End Select
Программисты
с опытом работы на С и Java, обратите внимание — команда break не нужна, поскольку
выполняется только одна секция Case. Дискретные наборы значений перечисляются
через запятую, а ключевое слово То позволяет задавать интервалы:
Select Case yourChoice
Case 1 To 9
' Порядок
Case -1. 0
' Неправильный ввод
End Select
Говоря об
управляющих конструкциях, нельзя обойти вниманием команду GoTo. Если перефразировать
старую шутку, современные программисты делятся на три группы: те, кто не знает,
как пользоваться GoTo, и знать не хочет; те, кто не знает, но переживает по
этому поводу; и те, кто умеет ею пользоваться.
Частое использование
GoTo приводит к многократным передачам управления и порождает «спагетти-код»,
который трудно читать и отлаживать. С другой стороны, в некоторые ситуациях
применение GoTo делает программу более понятной и логичной — например, если
в какой-то ситуации потребовалось выйти сразу из нескольких вложенных циклов.
Команда Exit для этого не подходит, поскольку она завершает только текущий цикл.
В
данной ситуации вместо Goto можно воспользоваться перехватом исключений (см.
главу 7), но некоторые программисты предпочитают классический подход.
Чтобы воспользоваться
командой GoTo в VB .NET, необходимо присвоить метку соответствующей строке.
Метка начинается в первой позиции строки, ее первым символом является буква,
а последним — двоеточие. Старайтесь присваивать меткам содержательные имена.
Пример:
Bad-Input:
' Фрагмент,
выполняемый при переходе
Предположим,
в нашей программе данные вводятся во вложенном цикле For. Чтобы завершить ввод,
пользователь вводит ZZZ:
SubMain()
Dim getData As String
Dim i, j As Integer
For i = 1 To 10
For j = 1 To 100
Console.Write("Type
the data, hit the Enter key between " & _
"ZZZ to end: ") getData = Console. ReadLine()
If getData =
"ZZZ" Then
Goto Bad Input
Else
' Обработка данных
End If
Next j
Next i
Exit Sub
BadInput:
Console.WriteLine("Data entry ended at user request")
Console. ReadLine()
End Sub
Выходить
из вложенного цикла командой Exit For неудобно — нам пришлось бы писать дополнительный
код для выхода из внешнего цикла. Обратите внимание: команда Exi t Sub предотвращает
передачу управления помеченному коду после завершения обоих циклов.
Начиная с
бета-версии 2 логические операторы (Not, And, Or и т. д.) работают на уровне
двоичных разрядов, как и в прежних версиях VB. Допустим, у вас имеются два целых
числа X и Y. Каждый бит результата X And Y равен 1 лишь в том случае, если равны
1 соответствующие биты обоих операндов; в противном случае бит результата равен
нулю. Таким образом, при вычислении результата X And Y вычисляется каждый бит
32-разрядного целого числа. Пример:
X = 7 'В двоичном представлении = 0111
Y = 12 'В двоичном
представлении = 1100
Выражение
X And Y в двоичной системе равно 0100 (4 в десятичной системе), поскольку лишь
во второй позиции оба бита равны 1. Остальные биты результата равны 0, поскольку
в этих позициях хотя бы один из битов операндов равен 0. Этот способ позволяет
проверить значения отдельных битов целого числа. Примеры:
(X And 1) =
1: проверить, установлен ли младший бит числа.
(X And 2) о
2: проверить, установлен ли предпоследний бит числа (поскольку в
двоичной
системе число 2 представляется записью 10).
X And 255: младший
байт числа (255 дес. = 11111111 дв.).
X And 65280:
старший байт числа (65280 дес. = 1111111100000000 дв.).
Значение,
предназначенное для проверки отдельных битов числа, называется маской (mask).
В VB .NET
имена массивов должны подчиняться тем же правилам, что и имена переменных. Ссылка
на элемент массива выглядит как имя массива, за которым в круглых скобках указывается
индекс.
Массивы VB
.NET во многом отличаются от массивов VB6. Одни изменения видны сразу, другие
не столь очевидны. Наиболее заметные изменения перечислены ниже.
Начиная с бета-версии 2 объявление 01m stri ngLi st(7) создает массив из восьми элементов с индексами от 0 до 7. Поскольку в VB .NET индексация всегда начинается с нуля, третий элемент массива обозначается stri ngList(2), а предшествующие элементы обозначаются stringList(0) и stringList(l).
Все массивы VB .NET
являются динамическими. Во время работы программы их можно переобъявить
с новым размером при помощи команд ReDim (с потерей текущего содержимого)
и ReDim Preserve (с сохранением текущего содержимого). Пример:
Dim x() As Single
ReDim x(20) ' Начиная
с бета-версии 2. создает массив из 21 элемента
ReDim Preserve x(50)
' 21 элемент сохраняется в массиве.
Команда
ReDim не позволяет изменять тип массива; также не допускается использование
ReDim при объявлении. Перед вызовом ReDim массив должен быть объявлен при помощи
Dim или аналогичной команды.
Dim weekend()
As String = {Saturday. Sunday}
Менее очевидные
изменения обусловлены тем, что массивы VB .NET являются экземплярами класса
Array. Подробности будут рассмотрены в главе 4, а пока достаточно указать, что
это позволяет выполнять операции с массивами вызовом методов класса Array. Ниже
продемонстрирован пример сортировки массива методом Sort:
Sub Main()
Dim stuff()
As Integer = (9. 7, 5, 4, 2. 1, -37, 6}
Array.Sort(stuff)
Dim i As Integer
For i = 0 To UBound(stuff)
Console.WriteLine(stuff(i))
Next
Console. ReadLine()
End Sub
Программа
выводит массив, отсортированный с применением чрезвычайно эффективного алгоритма
«быстрой сортировки».
VB.NET
наследует от .NET Framework некоторые очень полезные структуры данных, возможности
которых выходят далеко за рамки обычных массивов. На фоне этих структур коллекции
VB5 и последующих версий выглядят примитивно. В частности, списковые массивы
(с динамически изменяемыми размерами) и ассоциативные массивы (с доступом к
данным по ключу) часто оказываются удобнее обычных массивов. Многие из новых
структур данных рассматриваются в главах 5 и 6.
Массивы
с индексацией элементов в заданном интервале
Утверждение
о том, что индексация массивов всегда начинается с 0, не совсем точно.
Теоретически можно определять массивы с заданной верхней и нижней границей индекса,
но из-за неудобного синтаксиса и снижения быстродействия вряд ли вам захочется
это делать. В следующем фрагменте создается массив с индексацией элементов от
1995 до 2002:
Sub Main()
Dim anArray
As Array
Dim i As Integer
Dim i(0) As
Integer
Dim lowerBounds(0)
As Integer
i(O) = 7
lowerBounds(0)
= 1995 ' Создать массив с индексами 1995 - 2002
аnАrrау = Array.CreateInstance(GetType(System.Int32).
1. lowerBounds) anArray.SetValue(200000, 1995) anArray.SetValue(1000000. 2001)
Console.WriteLine("The entry in position 1995 is " & _ (anArray.GetValue(1995).ToString))
Console.WriteLine("The entry in position 2002 is " & _ (anArray.GetValue(2001).ToString))
Console. ReadLine()
End Sub
Присваивание
выполняется методом SetValue (значение,индекс), а чтение — методом GetValue(индекс).
Но если массив создается подобным образом в режиме жесткой проверки типов, вам
придется позаботиться о том, чтобы присваиваемое значение было преобразовано
к правильному типу!
Содержимое
массива часто перебирается в цикле от 0 до UBound(массив), однако вы также можете
воспользоваться конструкцией For-Each. Синтаксис For-Each выглядит следующим
образом:
For Each переменная
In массив
[команды]
[Exit For при
необходимости]
[команды] Next
Конструкция
For-Each универсальна и может использоваться в тех случаях, когда структура
данных поддерживает итеративный перебор элементов. За подробностями обращайтесь
к главе 4.
Microsoft
утверждает, что применение For-Each не будет приводить к существенному снижению
быстродействия по сравнению с For-Next (как это было в VB6).
Массивы не
ограничиваются одним измерением. Допустим, вы хотите сохранить таблицу умножения
в матричном виде. Примерное решение может выглядеть так:
Dim mulTable(11.11) As Integer
' Создает массив 12x12
Dim i As Integer, j As Integer
For i = 0 To
11
For j = 0 To
11
mulTable(i.j)
= (i+l)*(j+l)
Next j
Next i
Размеры массивов
в VB .NET могут изменяться, но количество измерений должно оставаться постоянным.
Многомерный
массив с неопределенным количеством элементов объявляется при помощи запятых.
Следующий пример показывает, как объявить трехмерный массив:
Dim salesByDivision(
, , ) As Decimal
Команда ReDim
задает или изменяет количество элементов в каждом измерении, но размерность
массива не изменяется.
При
сохранении содержимого массива командой ReDim Preserve допускается изменение
количества элементов только в последнем измерении массива.
Раньше выбор
между процедурой (Sub) и функцией (Function) определялся простым критерием:
если вы собирались использовать возвращаемое значение, следовало выбирать функцию,
а если нет — процедуру. Мы рекомендуем придерживаться этой модели, хотя ничто
не мешает проигнорировать возвращаемое значение функции. В объектно-ориентированных
программах функции и процедуры обычно включаются в классы и называются методами.
В VB .NET,
как и во многих языках программирования, существуют два способа передачи параметров
функциям и процедурам: передача по ссылке и передача по значению.
Когда параметр передается по ссылке, его изменения внутри функции приведут
к изменению исходного аргумента после выхода из функции. По умолчанию в VB .NET
параметры передаются по значению (а в VB6 — по ссылке).
Было
бы наивно полагать, что при передаче по значению исходный аргумент после выхода
из функции всегда сохраняет прежнее значение. В VB .NET состояние объекта может
измениться даже в том случае, если он передавался по значению. В главе 4 эта
ситуация, часто приводящая к возникновению хитроумных ошибок, рассматривается
более подробно.
Чтобы создать
новую функцию или процедуру в окне программы, установите курсор за пределами
других процедур и функций и начинайте вводить заголовок процедуры или функции.
Как только вы нажмете клавишу Enter, редактор IDE автоматически создаст команду
End правильного типа (End Functi on или End Sub). Ниже приведен заголовок функции,
которая получает целый параметр по значению и возвращает логическую величину
(True или False) в зависимости от того, принадлежит ли переданный параметр интервалу
от 1 до 10: Function IsBetweenlAnd10(ByVal num As Integer) As Boolean
В
режиме жесткой проверки типов (Option Strict) при объявлении функции необходимо
указывать тип возвращаемого значения (в нашем примере — Boolean).
Полный текст
модуля с функций Is Between lAnd 10 приведен ниже. Порядок следования функций
не важен — функция Sub Mai n может находиться и после определения функции, которая
в ней используется.
Module Modulel
Function IsBetweenlAnd10
(ByVal num As Integer) As Boolean
If num >= 1 And num <=10 Then
Return True
Else
Return False
End If
End Function
Sub Main()
Console. WriteLinedsBetweenlAnd100))
Console. ReadLine()
End Sub
End Module
В VB .NET
при вызове функции или процедуры непустой список параметров всегда заключается
в круглые скобки, как в строке с вызовом Console.WriteLine: IsBetweenlAnd100)
Обратите
внимание на ключевое слово Return. При выполнении команды Return функция завершается
и возвращает значение, указанное после Return (значение должно быть определенным
— возвращение аналога voi d не допускается). Также поддерживается синтаксис
с присваиванием имени функции, использовавшийся в прежних версиях VB:
Function IsBetweenlAnd10(ByVal num As Integer) As Boolean
If num >=
1 And num <= 10 Then
IsBetweenlAnd10
= True Else
IsBetweenlAnd10= False
End If
End Function
Использование
Return — дело вкуса. Команда Return нагляднее и проще, но старый синтаксис оставляет
управление внутри функции, а это иногда бывает удобно.
Обобщенная
форма определения функции выглядит следующим образом:
Function имя_функции
(аргумент1, аргумент2, ...) As тип
команды
Return выражение ' или имя_функции = выражение
End Function
где аргумент1
и аргумент2 — переменные. Имена функций подчиняются тем же правилам, что и имена
переменных. При вызове функции VB .NET выполняет команды, содержащиеся в определении
функции. Значение, указанное после Return (или последнее значение, присвоенное
имени функции), определяет результат вызова.
Хотя
возвращаемое значение обычно используется в программе, VB также позволяет вызвать
функцию простой командой вида foo(3) без присваивания.
Обычно количество
аргументов, передаваемых при вызове функции, должно совпадать с количеством
параметров в ее определении. Типы аргументов должны быть совместимы с типами
соответствующих параметров, при этом автоматически выполняются только расширяющие
преобразования. Например, следующий фрагмент допустим, поскольку преобразование
Short в Integer не приводит к потере данных:
Dim bar As Short = 3
Console.WriteLinedsBetweenlAnd10(bar))
VB .NET позволяет
создавать функции с переменным числом аргументов. Дополнительная информация
приведена далее в этой главе.
В отличие
от функций, процедуры не возвращают конкретных значений. Вызов процедур осуществляется
по имени. Непустые списки аргументов всегда заключаются в круглые скобки. В
приведенном ниже примере строка с вызовом процедуры выделена жирным шрифтом:
Option Strict
On
Module Modulel
Sub ShowBottlesOfBeer(ByVal
nbot As Integer)
Console.WriteLine(nbot
& " bottles of beer on the wall")
Console.Writeline(nbot
& " bottles of beer.")
Console.WriteLine("if
one of those bottles hsould happen to fall")
Console.WriteLine(nbot -1&" bottles of beer on the wall")
End Sub
Sub Main()
Dim I As Integer
For I = 10 To 1 Step -1
ShowBottlesOfBeer(I)
Next
Console.WriteLine("All
beer gone...")
Console. ReadLine()
End Sub
End Module
При вызове
процедур указывать ключевое слово Sub не обязательно. Строку с вызовом процедуры
из приведенного выше примера можно было записать и в таком виде:
Call ShowBottlesOfBeer(I)
Заголовок
процедуры должен содержать объявления всех параметров с ключевыми словами ByVal
или ByRef (по умолчанию используется ByVal, то есть передача по значению):
Sub имя_процедуры(В(ByVа1
аргумент1 As тип. ByVal аргумент2 As тип, ....)
команды
End Sub
При вызове
процедуры в форме имя_процедуры(аргумент1, аргумент2, ...) или Call имя_процедуры(аргумент1.
аргумент2, ...) VB .NET создает копии данных-аргументов и выполняет код,
содержащийся в теле процедуры (поскольку в отличие от предыдущих версий по умолчанию
параметры передаются по значению).
Преждевременный
выход из функций или процедур
В некоторых
ситуациях требуется покинуть функцию до того, как будет достигнута стандартная
точка выхода (например, если проверка покажет, что исходные данные
неверны и дальнейшие вычисления бессмысленны). Команда Return немедленно передает
управление вызывающей стороне (если не определена секция Finally — см. главу
7):
Function BallOut
(X As Double) As Double If X < 0 Then
Return 0'
Вернуть фиктивное значение Else
' Основные действия
End If
End Function
'Выход из процедур осуществляется командой
Exit Sub.
Передача
массивов функциям и процедурам
В VB .NET,
как и в прежних версиях VB, существуют удобные средства для работы с одномерными
и многомерными массивами в процедурах и функциях. Впрочем, существуют некоторые
нюансы, обусловленные передачей по ссылке и по значению; мы рассмотрим их в
главе 4. Перебор содержимого массива осуществляется конструкцией For Each или
(более распространенный вариант) стандартным циклом For с вычислением верхней
границы при помощи функции UBound (). Ниже приведен пример функции поиска максимального
элемента в массиве:
Function FindMax(ByVa1
a() As Integer
Dim finish As
Integer = UBound(a)
Dim max As Integer
= a(0)
Dim i As Integer
For i = 0 To
finish
If a(i) >
max Then max = a(i)
Next i
Return max End
Function
Обобщенная
форма вызова UBound(имя_массива, I) возвращает верхнюю границу по 1-му измерению
массива. Для одномерных массивов (списков) параметр 1 является необязательным.
Для
проверки также можно воспользоваться методом Length, реализованным в классе
массива, но этот метод возвращает количество элементов в массиве вместо верхней
границы (в многомерных массивах эти величины не совпадают).
Процедуры
и функции с необязательными аргументами
В VB. NET
сохранена возможность определения процедур и функций с необязательными аргументами,
но в отличие от VB6 для каждого необязательного параметра должно быть указано
значение по умолчанию. Следующий пример демонстрирует синтаксис объявления необязательных
параметров:
Sub ProcessAddress(TheName
As String,
Address As String. City As String. State As String.
ZipCode As String.
Optional ZipPlus4 As String = "0000")
В данном
примере последний параметр является необязательным (Optional) и по умолчанию
равен "0000".
В
главе 4 описана перегрузка (overloading) —другой способ определения функций
с необязательными параметрами.
VB .NET также
позволяет определять процедуры и функции с произвольным количеством аргументов.
Для этого в качестве параметра передается массив с ключевым словом РаramАrrау,
как в следующем примере:
Function AddThemUp(ByVal
ParamArray stuff() As Double) As Double
Dim total As
Double = 0
Dim Number As
Double = 0
Dim I As Integer
For I = 0 To UBound(stuff)
total = total
+ stuff(I)
Next
Return total
End Function
Пример использования
функции:
x = AddThemUp(3,
4. 5. 6)
В результате
переменной х присваивается значение 18.
При вызове
функций и процедур с большим количеством параметров (особенно необязательных)
существует такая элегантная возможность, как передача именованных аргументов.
Если значения параметров при вызове передаются в виде «имя -:=значение»,
вам не придется беспокоиться о соблюдении порядка аргументов (регистр символов
в именах игнорируется). В отличие от прежних версий VB, где именованные аргументы
то работали, то нет, в VB .NET они работают всегда.
Именованные
аргументы разделяются запятыми. При разумном выборе имен параметров именованные
аргументы заметно упрощают чтение программы, особенно при большом количестве
необязательных аргументов. Для примера возьмем приведенный выше заголовок функции
ProcessAddress:
Sub ProcessAddress(TheName
As String.
Address As String. City As String. State As String,
ZipCode As String,
Optional ZipPlus4 As String = "0000")
Вызов этой
процедуры может выглядеть так:
ProcessAddress(Address
:= "The Whitehouse"
Name := "GeorgeW",
City := "DC".
_
State:= String.Empty.
_
ZipCode:= "12345")
Обратите
внимание: порядок перечисления аргументов отличается от заданного в заголовке
процедуры.
В VB .NET,
как и в любом сколько-нибудь серьезном языке программирования, поддерживается
рекурсия — решение задач посредством сведения их к более простым задачам
того же типа. Одним из стандартных примеров рекурсивного решения является перебор
дерева каталогов на диске (см. главу 9).
На концептуальном
уровне рекурсивное решение выглядит следующим образом:
Решение задачи
с применением рекурсии
If задача тривиальна
Решить Else
Упростить задачу,
сводя ее к однотипной, но более простой задаче
Решить более простую задачу с применением рекурсии
End If
(Возможно) Объединить
решение простой задачи (или задач)
с решением исходной
задачи.
Рекурсивная
функция или процедура постоянно вызывает сама себя, непрерывно упрощая задачу
до тех пор, пока ее решение не станет тривиальным; в этот момент задача решается,
и происходит возврат к предыдущему уровню. Применение рекурсии нередко связано
с принципиальным изменением подхода к некоторым задачам, порождающим особенно
элегантные решения и столь же элегантные программы (кстати, многие алгоритмы
сортировки — такие, как встроенный метод Sort класса .NET Array, — основаны
на принципе рекурсии).
В качестве
примера мы рассмотрим программу поиска наибольшего общего делителя двух целых
чисел (то есть наибольшего целого числа, на которое они оба делятся без остатка).
Пример:
Около 2000 лет назад
Евклид предложил следующий алгоритм вычисления НОД двух целых чисел а и
b:
Вспомним,
что функция Mod возвращает остаток от целочисленного деления, а выражение a
Mod b,равно 0 лишь в том случае, если а кратно b. Пример:
НОД(126.12)=НОД
(12. 126 Mod 12)=НОД (12,6) - 6
Ниже приведен
пример рекурсивной функции для вычисления НОД. В строке, выделенной жирным шрифтом,
функция GCD вызывает сама себя для более простого случая:
Option Strict
On Module Modulel
Function GCD(ByVal p As Long, ByVal q As Long) As Long
If Q Mod P =
0 Then
Return P Else
Return GCD(Q, P Mod Q)
End If
End Function
Public Sub Main()
Console.WriteLine("The GCD of 36 and 99 is " & GCD(36. 99))
Console. ReadLine()
End Sub
End Module
Сначала обрабатывается тривиальный случай Q Mod P = 0. Если это условие не выполняется, функция GCD снова вызывает себя для более простого случая, поскольку в результате применения Mod мы переходим к меньшим числам. В приведенном примере объединять результаты не нужно (как, например, в функции сортировки).