Самоучитель по VB.NET
Сайт Алексея Муртазина (Star Cat) E-mail: starcat @ nm.ru
Мои программы Новости сайта Мои идеи Мои стихи Форум Моё фото Мой ЖЖ
Заработай!!!VB коды Статьи о VB6 API функции VB.NET
Более 3000 ссылок Интернет Все работы с фото и видео
Сайт о моём деде Муртазине ГР Картинная галерея "Дыхание души"
Звёздный Кот

Самоучитель по VB.NET
Глава 8.

Типы данных и операторы

Часть 1 этой книги посвящалась стратегическим вопросам, связанным с платформой .NET: что это такое, какое место она займет в мире Microsoft и в вашем собственном мире. В части 2 рассматривались ключевые концепции языкового уровня, которые необходимо твердо усвоить, чтобы старый стиль программирования не приводил к возникновению серьезных недочетов на стадии проектирования.

Эта часть книги посвящена программам, а точнее, изменениям в самом языке. Основное внимание будет уделяться не изменениям синтаксиса, а их последствиям и влиянию на стиль программирования.

Чтобы лучше представить масштаб изменений, с которыми предстоит столкнуться программистам Visual Basic, мы начнем с рассмотрения фундаментальных типов данных языка.

 

Числовые типы

В табл. 8.1 приведен список числовых типов данных, поддерживаемых в VB6 и VB .NET. Число в скобках указывает размер данных в байтах. В последнем столбце приводится имя типа переменной в CLR — исполнительной среде, используемой VB .NET и другими языками .NET.

Таблица 8.1. Числовые типы данных, поддерживаемые VB6 и VB .NET

Тип VB6

Тип VB .NET

Тип CLR

Byte(l)

Byte(l)

System. Byte

String* 1(1)

Char(2)

System. Char

Boolean(2)

Boolean(4)

System. Boolean

Нет

Decimal(12)

System. Decimal

Currency(S)

Нет

Нет

Double(8)

Double(8)

System. Double

Integer(2)

Short(2)

System.Int16

Long(4)

Integer(4)

System.Int32

Нет

Long(8)

System.Int64

Single(4)

Single(4)

System.Single

Присмотритесь повнимательнее. Если у вас и были сомнения относительно того, действительно ли VB .NET является новым языком, при взгляде на эту таблицу они должны были рассеяться. Модификация фундаментальных числовых типов языка — это действительно серьезное изменение. Конечно, программа преобразования обеспечивает автоматическую адаптацию большинства типов данных, но факт остается фактом: программы, написанные на VB .NET, сильно отличаются от программ на VB6.

 

String*1 и Char

В VB6 не существует типа Char. Почему же символьный тип понадобился в VB .NET? Потому что в VB .NET не существует строк фиксированной длины (эта тема рассматривается далее в этой главе). Конечно, можно использовать динамическую строку и работать с первым символом, но это слишком расточительно, поскольку все строки VB .NET создаются в куче. Конечно, выделение памяти из кучи в VB .NET выполняется очень быстро, но по эффективности эта операция все же уступает выделению памяти в стеке для типа Char. Тип Char является 16-разрядным, чтобы он мог использоваться для представления символов Unicode. По этой причине я и разместил его среди числовых типов данных, хотя в действительности четко классифицировать его довольно трудно.

 

Логические величины

Логические, величины (Boolean) теперь занимают 4 байта вместо двух. Впрочем, этот факт не должен влиять на работу большинства программ.

Помню, на первых порах существования Visual Basic шли жаркие споры относительно природы переменной Boolean. Дело в том, что переменная Boolean поддерживает всего два значения (True и False, представленные значениями -1 и 0), но Visual Basic 6 выполняет с переменными типа Integer поразрядные операции.

Например, в следующем фрагменте:

If 4 And 8 Then

Debug.Print "Should be true" 

Else

условие считается ложным, и строка не выводится. Операторы VB6 And и Or работают с числами не как с логическими величинами, а как с совокупностью разрядов. Применительно к числам они работают правильно лишь в том случае, если True всегда представляется -l. a False всегда представляется 0. Логические операции между числами часто дают логически непоследовательные результаты. В этом случае числа 4 и 8 (отличные от 0) с позиций логики относятся к True, поэтому выражение 4 And 8 вроде бы должно быть равно True. Тем не менее результат поразрядной операции 4 And 8 равен 0, то есть False.

Это может приводить к ошибкам, особенно при использовании функций API, возвращающих 1 в случае удачного завершения. Например, функция API IsWindow возвращает 1 для действительного манипулятора окна. Иначе говоря:

В VB .NET используется объект System.Boolean, принимающий только два значения: TruenFalse. B версии бета-1 компания Microsoft героически попыталась внести толику здравого смысла в традиционные правила работы с логическими переменными в Visual Basic и переопределила операторы And и Or как логические вместо поразрядных. Кроме того, для выполнения поразрядных операций были определены новые операторы BitAnd и BitOr. При таком разграничении логические операции Not, And и Or всегда работают только с логическими типами (или переменными, преобразованными к логическому типу), что устраняет все возможные неоднозначности. Если вы хотите выполнить поразрядную операцию, используйте BitNot, BitAnd и BitOr.

К сожалению, некоторым недальновидным программистам VB6 удалось убедить Microsoft, что это изменение каким-то образом нарушает те принципы, благодаря которым Visual Basic завоевал такую популярность, и Microsoft по столь же непостижимым причинам уступила.

В результате в VB .NET True по-прежнему определяется как -1, а логические и поразрядные операции выполняются одной командой.

В этом нетрудно убедиться при помощи проекта Boolean (листинг 8.1).

Листинг 8.1. Проект Boolean1 

' Демонстрация работы с логическими переменными 

' Copyright ©2001 by Desaware Inc.

 ' AH Rights Reserved.

 Module Modulel

Sub Main()

Dim A As Boolean

Dim I As Integer

A = True

I = Clnt(A)

Console.WriteLine ("Value of Boolean in Integer is: " + I .ToString())

Console.WriteLine ("Value of a System Boolean in Integer is: " + I.ToString())

I = 5

A = CBool(I)

Console.Wri teLine ("Value of Boolean assigned from 5 is : " + A.ToStringO) I = Clnt(A)

Console.Wri teLine ("And converted back to Integer: " + I .ToString()) Console.WriteLine("5 And 8: " + (CBool(5) And CBool(8)).ToString) Console.WriteLine("5 And 8: " + (5 And 8) .ToString())

 Console.ReadLine () 

End Sub

End Module

1 Все исходные тексты можно найти на сайте издательства «Питер» www.piter.com. — Примеч. ред.

Результат выглядит следующим образом:

Value of Boolean in Integer is: 1

Value of Boolean assigned from 5 is : True

And converted back to Boolean: 1

4 And 8: True

4 BitAnd 8: 0

Логические переменные могут принимать два значения: True и False. Пока логические операции выполняются с логическими переменными, все замечательно. Но помните: когда VB .NET встречает числовую переменную вместо логической, он выполняет с ней поразрядную операцию, что может приводить к возникновению непредвиденных ошибок.

 

Currency и Decimal

На смену Currency пришел тип Decimal. Он обеспечивает большую точность (12 байтов вместо 8), а позиция десятичной запятой не фиксируется. Вряд ли это изменение окажет сколько-нибудь заметное влияние на ваши программы. Основное преимущество такого изменения заключается в том, что Decimal принадлежит к числу «родных» типов .NET и поэтому поддерживается любыми компонентами во всех языках .NET.

 

Integer, Long и Short

Расширение числовых типов (с 16 до 32 разрядов для типа Integer, с 32 до 64 разрядов для типа Long) — одно из нововведений VB .NET, вызывающих неоднозначную реакцию. В VB тип Integer всегда представлялся 16-разрядной величиной, и это было вполне нормально, пока программа ограничивалась VB и не пользовалась внешним сервисом. Однако в 32-разрядном мире C++ (а следовательно, и во всей документации Microsoft) целочисленный тип представляется 32 битами, поэтому программистам VB постоянно приходилось помнить о том, что «короткий» тип API соответствует типу VB Integer, а «целый» тип API соответствует типу VB Long.

При проектировании VB .NET Microsoft оказалась перед выбором. Можно было сделать тип Integer 32-разрядным и обеспечить единую интерпретацию термина «Integer» во всех языках и документации .NET. С другой стороны, можно было ограничить тип VB Integer 16 битами и ввести новый тип Int64.

Изменение определения Integer приводит документацию VB в соответствие с остальной документацией .NET. С другой стороны, это несколько затрудняет работу программистов, привыкших программировать на разных языках, поскольку им приходится постоянно мысленно переключаться из VB6 в VB .NET и обратно.

Лично я одобряю это изменение. Думаю, в конечном счете согласованность типов перевесит все мелкие неудобства. Впрочем, моя точка зрения субъективна: для человека, которому приходится постоянно работать с Win32 API, согласованность типов важнее сохранения старых привычек программирования. Но я готов признать, что это решение было неоднозначным, и против него можно привести обоснованные возражения.

При программировании в VB .NET следует помнить, что в 32-разрядных операционных системах тип Integer является самым эффективным числовым типом данных. Старайтесь избегать типа Long, если только вам действительно не требуется 64-разрядное представление числа.

Также приготовьтесь к тому, что в какой-нибудь предстоящей 64-разрядной версии CLR тип Integer будет переопределен как 64-разрядный. Вероятно, к этому моменту Microsoft Определит специальный 32-разрядный тип данных для работы с 32-разрядными переменными.

 

Беззнаковые типы

Беззнаковые переменные в VB .NET не поддерживаются. Впрочем, не спешите огорчаться. Учтите, что беззнаковые типы, определенные в CLR, не соответствуют спецификации CLS. Другими словами, если вы используете беззнаковую переменную в открытом методе, свойстве или структуре данных сборки, нет гарантий, что другой CLS-совместимый язык сможет воспользоваться этой сборкой.

Следовательно, для большинства программистов беззнаковые переменные (если бы они существовали в VB .NET) могли бы использоваться лишь в границах отдельной сборки.

В VB6 реальная потребность в беззнаковых типах данных возникала только при работе с функциями Win32 API. Как вы вскоре убедитесь, в VB .NET эта потребность практически исчезла, поскольку программы в основном ориентируются на работу с классами .NET Framework, нежели на прямой вызов функций API. При использовании функций API пространство имен PInvoke обеспечивает автоматическое преобразование знаковых переменных (см. главу 15).

Итак, некоторым программистам будет не хватать беззнаковых типов данных. Возможно, для кого-то это даже станет аргументом в пользу С# перед VB .NET, но это слишком слабый аргумент.

Типы CLR

В третьем столбце табл. 8.1 приведены имена типов данных, используемые CLR. Я привел их для того, чтобы подчеркнуть одну из важных особенностей .NET, особо выделяемых Microsoft, — совместимость языков программирования.

К сожалению, в изложении Microsoft это выглядит довольно глупо. Особое внимание почему-то уделяется удобству построения приложений с применением разных .NET-языков. Но поскольку три основных языка .NET (C++, С# и VB .NET) обладают примерно одинаковыми возможностями, скорее всего, большинство программистов выберет самый привычный язык и будет пользоваться им.

На совместимость языков можно взглянуть и с другой, более правильной точки зрения. Совместная работа компонентов, написанных на разных языках, играет очень важную роль в .NET и является огромным шагом вперед по сравнению с библиотеками типов СОМ или ручным преобразованием, выполняемым при использовании команд Deри вызове функций API.

Как видно из табл. 8.1, каждому типу данных VB .NET соответствует определенный тип данных CLR, что позволяет использовать его в других языках .NET, соответствующих спецификации CLS.

 

Другие типы данных

Изменения, внесенные Microsoft, не ограничиваются числовыми типами данных.

Прощай, Variant (наконец-то!)

Я никогда не любил универсальный тип Variant. Не стану углубляться в объяснение причин, так как эта тема рассматривается в одной из предыдущих книг, посвященных разработке компонентов COM/ActiveX. Ограничусь кратким перечнем.

В Visual Basic 6 универсальный тип данных реально использовался только там, где это было неизбежно, например при работе с базами данных или объектными моделями, в которых этот тип был задействован, а также при ограниченной перегрузке параметров (что позволяло передавать одной функции параметры нескольких типов1).

1 В языке VB .NET предусмотрена прямая поддержка этой возможности.

 

Универсальный тип и объекты

Некоторые программисты склонны рассматривать тип Object как своего рода замену универсального типа, поскольку он позволяет ссылаться на произвольные данные. Даже в документации Microsoft упоминается, что тип Object заменяет Variant. На самом деле это неточно.

Переменная универсального типа содержит ссылку на блок памяти с произвольными данными. Сам блок памяти содержит поле, определяющее тип хранимых данных. Подсистема OLE поддерживает функции для очистки, присваивания и преобразования значений универсальных типов.

На рис. 8.1 показано, что происходит при объявлении переменной типа Variant с последующим присваиванием ей числового и строкового значения.

Рис. 8.1. Изменение содержимого переменной типа Variant при присваивании 

Как видите, в результате присваивания содержимое переменной Variant изменяется, а внутренний признак типа приводится в соответствие с новым типом данных.

На рис. 8.2 изображен совершенно другой процесс, сопровождающий присваивание объектной переменной в VB .NET.

Рис. 8.2. Присваивание объектной переменной изменяет ссылку

При первоначальном присваивании х=5 целочисленное значение упаковывается в объект, содержащий величину 5. Переменная х содержит указатель (ссылку) на упакованное целое число. При следующем присваивании строковой величины х присваивается ссылка на объект String, созданный в куче. Что происходит с объектом Integer? Ссылки на него утрачены, и этот объект уничтожается при следующей сборке мусора.

Возможность ссылаться на любые типы переменных из типа Object объясняется тем фактом, что все типы данных .NET Framework являются производными от типа Objеct и не имеют никакого отношения к универсальному типу Variant.

Проверка типа объекта

Хотя тип Object и не является универсальным, он позволяет создавать общие переменные, ссылающиеся на объекты разных типов. По аналогии с оператором VarType, позволяющим определить тип переменной, можно определить тип объекта, на который ссылается переменная типа Object (и вообще любая переменная). При этом используется механизм получения общей информации об объектах, их методах и свойствах, а также других метаданных сборки. Этот механизм называется рефлексией (reflection). Рефлексия подробно рассматривается в главе 11.

Получение информации о типе произвольного объекта основано на использовании метода Get Туре, класса System. Object (базовый класс любого объекта .NET). Данная возможность продемонстрирована в простой программе DataTypes.

Module Modulel

 Sub Main()

Dim о As Object

o = 5

console.WriteLine (o)

console.WriteLine (o.GetType.Full Name)

о = "Hello"

console.WriteLine (o)

console.WriteLine (o.GetType.FullName)

console. Re adLine()

 End Sub 

End Module

Программа выводит следующий результат:

5

System.Int32

Hello

System.String

Объект Type, возвращаемый методом GetType, содержит ряд свойств, предназначенных для получения информации об объекте или классе. При помощи свойства FullName можно получить полное имя объекта. Метод GetTypeCode возвращает числовой код типа, с которым удобнее и эффективнее работать в программе.

Прочее

Если при умножении двух объектов Integer (32-разрядных) происходит переполнение, то результатом будет величина типа Long (64-разрядная).

 

Строки

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

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

Но самый важный аргумент в пользу неизменности строк, о котором многие и не задумываются, связан с особенностями передачи объектов в качестве параметров. Мы еще вернемся к этой теме в настоящей главе.

Для удобного и эффективного конструирования строк можно воспользоваться классом System.Text. StringBuilder. Тем не менее программисты Visual Basic, вероятно, предпочтут чрезвычайно гибкую команду Mid, с которой еще проще работать.

Пример Strings (листинг 8.2) показывает, как организовано совместное использование строковых литералов в VB .NET.

Листинг 8.2. Пример Strings

Module Modulel

Sub Main()

 Dim A As String 

Dim В As String 

A = "Hello" 

В = "Hel" + "lo"

 Console.WriteLine (A = B)

 Console.Writeline (A Is B)

Mid(A. 1, 1) = "X"

  Mid(B, 1, 1) = "X"

 Console.WriteLine (A = B)

 Console.WriteLine (A Is B) 

Console.WriteLine (A) 

Console.ReadLine() 

End Sub

End Module

Результат выглядит так:

True

True

True

False

Xello

Строка Hello присваивается переменным А и В на стадии компиляции (у компилятора хватает сообразительности понять, что В присваивается уже существующий строковый литерал, и использовать ту же ссылку). Переменные А и В не только имеют одинаковые значения, но и ссылаются на один и тот же объект (как видно из проверки условия A Is В). Если бы строки могли модифицироваться, подобное совмещение литералов в VB .NET оказалось бы невозможным, поскольку изменение одной переменной отражалось бы на другой переменной.

После модификации первого-символа в обеих строках переменные остаются равными, но уже не ссылаются на один и тот же объект.

В VB .NET была исключена функция String$, поскольку объект String содержит ряд удобных конструкторов. Например, для создания строки длины N, состоящей из одних нуль символов, можно воспользоваться следующей командой:

 s = New String(Chr(0), N)

Массивы

В области работы с массивами в VB .NET наблюдаются два принципиальных изменения. Первое чрезвычайно важно, а второе для большинства программистов относительно несущественно.

Индексация массивов теперь начинается с нуля. Это означает, что строка

 Dim X(10) As Integer

всегда создает массив из 11 элементов с индексами от 0 до 10. Она эквивалентна следующему объявлению VB6:

 Dim X(0 То 10) As Integer

Вероятно, это огорчит многих программистов VB, которым назначение начального индекса массива кажется удобным. Программисты С# и C++ к подобной схеме индексации уже успели привыкнуть. С точки зрения программиста это изменение полезно тем, что оно обеспечивает единый стиль работы с массивами. Смена базовых индексов в приложении чревата ошибками, поскольку программисту приходится постоянно помнить о том, какой индекс используется в каждом конкретном случае, и переключаться между ними. Применение постоянного базового индекса упрощает чтение программы и помогает разобраться в чужом коде. Вероятно, многие программисты VB .NET без труда привыкнут к нулевой индексации или будут использовать элементы, начиная с первого, попросту игнорируя нулевой элемент.

Обычно я с энтузиазмом отношусь ко всем аспектам языка, защищающим программиста от ошибок, но, честно говоря, мне кажется, что на этот раз Microsoft просчиталась. Сначала я подумал, что это изменение как-то связано со спецификой .NET Framework, однако после знакомства с классом Array выяснилось, что он поддерживает смену базового индекса. В этом нетрудно убедиться при помощи примера, приведенного в листинге 8.3.

Листинг 8.3. Приложение ArrayExample 

Module Module1

Sub Main()

Dim X As Array

Dim I As Integer

Dim Lengths(O) As Integer

Dim LowerBounds(O) As Integer

Lengths(0) = 10 LowerBounds(O) = 1

X = Array.CreateInstance(GetType(System.Int32), _ 

Lengths, LowerBounds)

Try

X.SetValue(5, 0)

 Catch e As Exception

Console.WriteLine (e.Message) 

End Try

X.SetValue(6, 1)

Console.WriteLine (X.GetValue(1)

Console. ReadLine()

End Sub 

End Module

Все массивы .NET, в том числе и массивы VB, основаны на типе Array. Как в этом убедиться? Потому что для массива, объявленного в Visual Basic, можно вызывать все методы объекта Array. В листинге 8.3 мы объявляем переменную-массив X и создаем экземпляр целочисленного массива длины 10 с базовым индексом, равным 1. После этого программа пытается присвоить значения двум элементам массива с индексами 0 (этот элемент не существует) и 1. Результат выглядит так:

An exception of type System.IndexOutOfRangeException was thrown. 6

При обращении к элементу с индексом 0 инициируется исключение IndexOutOfRangeException. С индексом 1 все нормально.

Приведенный код выглядит неуклюже, но он убедительно доказывает, что смена базового индекса поддерживается на уровне .NET Framework.

Почему же эта возможность была исключена из VB .NET?

Хотя объект .NET Framework Array поддерживает изменение базового индекса, спецификация CLS требует, чтобы индексация массивов всегда начиналась с нуля. В CLS определяется минимальный набор требований, который обеспечивает возможность свободного использования сборки в любом другом CLS-совмес-тимом языке.

Microsoft оказалась перед выбором. С одной стороны, можно было включить изменение базового индекса в CLS и оставить эту возможность в VB .NET; с другой стороны, можно было сохранить CLS в прежнем виде и каким-то образом решить проблему с созданием CLS-несовместимых сборок в VB .NET.

Что ж, решение Microsoft вполне очевидно. Лично я предпочел бы добавить эту возможность в C++ и С#, нежели исключать ее из VB. Конечно, принятое решение, каким бы болезненным оно ни было, прежде всего направлено на обеспечение CLS-совместимости для программистов VB .NET, особенно широко использующих компоненты в процессе программирования.

Все массивы VB .NET могут переобъявляться с изменением размера. Вероятно, для большинства программистов VB это изменение несущественно, однако оно сказывается на работе с функциями API (см. главу 15).

 

Дата

При работе с датой и временем в VB .NET используется тип объекта System. DateTime. Для программистов VB самое существенное последствие заключается в том, что теперь не существует простого преобразования даты в Double и обратно. Впрочем, класс DateTime содержит методы для преобразования данных в формат OLE Automation. К счастью, объект DateTime обладает очень богатыми возможностями, включая средства для сравнения даты/времени, а также прибавления и вычитания временных интервалов.

 

Перечисляемые типы

Перечисления почти не изменились со времен VB6 — во всяком случае, с точки зрения синтаксиса. Главное изменение заключается в том, что Enum теперь интерпретируется как любой другой объявленный тип. Иначе говоря, перед ключевым словом Enum теперь могут находиться указатели класса доступа Public, Private, Protected и Friend, а также ключевое слово Shadows, управляющее видимостью Enum в производных классах и других сборках.

В VB .NET базовый класс Enum содержит разнообразные методы для прямых и обратных преобразований между непосредственным значением и его строковым (именованным) представлением.

С точки зрения программирования единственным реальным изменением является поддержка атрибута Flags, использование которого продемонстрировано в программе EnumExample (листинг 8.4).

Листинг 8.4. Приложение EnumExample 

Module Modulel

Enum E

A = 5

В

С = б ' Обе переменные, В и С, равны 6

 End Enum

<Flags()> Enum В

A = &H1

В = &H2

С = &H4

 End Enum

Enum С

A = &H1

В = &Н2

С = &H4 

End Enum

Sub F(ByVal X As E) 

End Sub

Sub FBCByVal X As B)

 End Sub

Sub Ma1n()

Dim II, 12, 13 As Integer И = E.A: 12 = E.B: 13 = E.C

' Метод ToStMng нельзя использовать непосредственно

' для переменной перечисляемого типа, поскольку

' он вернет имя перечисления.

Console.WriteLine (II.ToString() + 12.ToString() + 13.ToString())

 F (E.C)

FB (B.A Or B.C Or B.B)

Console.WriteLine (E.Format(GetType(E), 5, "G"))

 Console.WriteLine (C.Format(GetType(C), (C.AOrC.C), "G"))

 Console.WriteLine (B.Format(GetType(B), (B.A Or B.C), "G"))

 Console.ReadLine()

 End Sub

End Module

Программа доказывает, что перечисляемый тип может содержать несколько элементов с одинаковым значением (в нашем примере Е . В и Е . С равны 6). Метод Format типа Enum возвращает имя заданной величины в перечислении — эта информация часто используется в процессе отладки или при построении отчетов.

566

 А

Перечисляемые типы обычно используются в двух ситуациях: при логической группировке констант, следующих в некоторой последовательности, и при создании списков флагов — констант с одним установленным битом, объединяемых оператором Or. Эти два типичных применения Enum продемонстрированы выше в функциях F и FB.

Атрибут Flags типа Enum сообщает исполнительной среде о том, что параметры Enum объединяются при помощи поразрядных логических операций. Перед тем как возвращать результат1, метод Format читает значение этого атрибута, используя механизм рефлексии. Таким образом, для переменной С возвращается значение величины, но для переменной В с установленным атрибутом Flags будет возвращена строка А | С — поразрядная дизъюнкция двух переменных в синтаксисе С++/С#. Следовательно, результат будет выглядеть так:

5 А,

 С2

1 В качестве параметров оператору Format передается тип перечисления, форматируемая величина и код формата (код G означает, что метод должен возвращать имя величины в перечисляемом типе, если это возможно).

2Согласно документации должна возвращаться строка «А|С», но версия бета-2 возвращает «А,С». Что будет в окончательной версии? Будущее покажет.

Объявления

С объявлениями переменных в VB .NET все просто и мило: язык позволяет объявлять несколько переменных в одной строке (см. приложение Dent>). Впрочем, на самом деле это несущественно.

VB .NET позволяет инициализировать переменные и массивы при объявлении, как показано ниже для переменных С и D. Все выглядит вполне логично: 

Module Modulel

Sub Main()

Dim А, В As Integer

Dint С As Integer = 6

Dim D() As String = {"A", "B", "C", "D", "E"}

Dim E As Integer, F As String 

End Sub

End Module

Списки инициализации массивов заключаются в фигурные скобки, которые впервые вошли в синтаксис Visual Basic.

Объявления DefType (Deflnt, DefLong и т. д.) теперь не поддерживаются. Впрочем, это небольшая потеря, поскольку они всего лишь упрощали чтение программ и обеспечивали единый стиль выбора имен в приложениях VB.

Преобразования и проверка типа

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

В Visual Basic 6 поддерживается так называемое «злостное искажение типов» (evil type coercion). Ниже приведен классический пример.

Sub Evil ()

Debug.Print 15 + "15" + "15"

 End Sub

Sub Hain()

Evil End Sub

При выполнении этого фрагмента в окне отладки выводится число 45.

Встретив выражение со смешанными типами, VB6 пытается угадать, какие же преобразования вы подразумевали. В простых и очевидных случаях (например, при преобразовании Long в Integer) он действует вполне логично.

К сожалению, иногда VB6 угадывает неправильно и не предупреждает вас о том, что преобразование может привести к ошибкам при выполнении программы.

Некоторые считают, что это не такая уж серьезная проблема. Обычно VB6 угадывает правильно, а опытный программист способен заранее предвидеть самые очевидные проблемы. Автоматическое преобразование типов подавляет выдачу предупреждений, большинство из которых все равно игнорируется — ведь программист знает, что он делает.

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

Если ошибка пережила стадию начальной разработки, затраты на ее исправление заметно возрастают. Они складываются из затрат на отслеживание ошибки, на взаимодействие между разработчиками, пытающимися найти ошибку и поручить ее исправление соответствующему работнику, на проверку всех исправлений и регрессионное тестирование всей программы, защищающее от случайного внесения новых ошибок.

Но если ошибка пережила все предварительные стадии и добралась до конечного продукта, затраты достигают астрономических величин.

В VB .NET появился новый флаг жесткой проверки типов; к сожалению, он не устанавливается по умолчанию в новых проектах VB .NET. Привыкните к тому, чтобы работа над новым проектом всегда начиналась с установки этого флажка1.

1 В версии бета-1 флажок жесткой проверки типов устанавливался по умолчанию. К сожалению, в версии бета-2 Microsoft решила, что по умолчанию этот флажок следует сбрасывать. В обычной ситуации я бы высказался покрепче, но я так рад, что ятот флажок вообще существует, что воздержусь от сильных выражений... Хотя это, конечно, чудовищная ошибка.

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

Я твердо в этом убежден, поэтому флажок Strict Type Checking устанавливается во всех примерах этой книги. Более того, при обсуждении перехода на VB .NET я даже не рассматриваю возможность его сброса. Единственный случай, когда об этом стоит подумать хотя бы теоретически — при непосредственной адаптации кода мастером Upgrade Wizard. Во всем новом коде и в большинстве адаптированных программ этот флажок следует устанавливать.

Правила жесткой проверки типов весьма просты. При каждом преобразовании переменной от одного типа к другому компилятор проверяет, может ли это / привести к потере данных и возникновению ошибок во время выполнения программы. Для примера рассмотрим листинг 8.5.

Листинг 8.5. Приложение Conversions

Module Modulel

Sub Main()

Dim I As Integer, L As Long I = 50 L = I

Console.WriteLine (L) L = 50

I = Clnt(L) ' Необходимо явное преобразование. 

Console.WriteLine (I) L = &H100000000 

Try

I = CType(L, Integer)

 Catch E As Exception

Console.WriteLine (E.Message)

 End Try

Console.WriteLine (I)

 Console. ReadLine()

End Sub

 End Module

Тип Integer может быть неявно преобразован к типу Long, так как последний способен вместить все допустимые значения типа Integer. Однако обратное неверно. При выполнении этой программы будет выведен следующий результат:

50

50

An exception of Type System.OverflowException was thrown.

50

В листинге 8.5 продемонстрированы два способа преобразования Integer в Long: функции CInt и СТуре. Эти функции сообщают компилятору, что вы действительно знаете, что делаете, присваивая значение Long переменной типа Integer. Без них на стадии компиляции возникли бы ошибки. В нашем примере функции CInt и СТуре делают одно и то же, просто функция СТуре является более универсальной.

 

Преобразования и классы

Как было показано в главе 5, функция СТуре преобразует объекты от одного типа к другому. Правила явных и неявных преобразований объектов аналогичны правилам преобразования для переменных структурных типов.

Производный класс может быть неявно преобразован к базовому классу. Это вполне логично, поскольку производный класс является «частным случаем» базового класса и содержит все его свойства и методы.

Также допускается неявное преобразование класса к интерфейсу, реализованному этим классом.

Преобразование от базового класса к производному должно быть явным. Поскольку нет гарантии, что полученный объект действительно относится к производному классу, неудачное преобразование может привести к ошибкам во время выполнения программы.

Преобразование от интерфейса к классу тоже должно быть явным. Успех этого преобразования не гарантирован.

Программа ObjectConversions (листинг 8.6) демонстрирует правила преобразования объектов. В ней определяется интерфейс I, класс А (реализующий интерфейс I) и класс В, производный от А. В комментариях указаны правила, действующие в каждом конкретном случае.

Листинг 8.6. Приложение ObjectConversions

Module Modulel

 Interface I

Sub Mylnterf aceFunc()

 End Interface

Implements I

Protected Overridable Sub MyFunc()

End Sub

Public Sub MyPublicFunc()

End Sub

Public Sub MylnterfaceFunc() Implements I .MylnterfaceFunc

End Sub 

End

ts A

End

Sub Main() D

im myA As New A()

 Dim myB As New B()

 Dim myAReference As A

 Dim myBReference As В 

Dim mylReference As I

myAReference = myA ' Same type - ok

myAReference = myB ' Базовый тип - неявное преобразование

mylReference = myA ' Реализованный интерфейс - неявное

myA = CType(myIReference, A) ' Ссылка может относиться 

' к любому объекту - явное преобразование. 

' На стадии компиляции нельзя определить, 

' будет ли работать эта команда

Try

myB = CType(myIReference, В) ' Не работает

 Catch e As Exception

Console.WriteLine (e.Message)

 End Try

mylReference = myB ' Реализованный интерфейс - неявное 

' Класс В, производный от А, наследует интерфейс I.

Console.ReadLine() 

End Sub

 End Module

 

Преобразования и структуры

На момент написания книги VB .NET не поддерживал нестандартных преобразований для структур, определяемых в программе. Например, если вы создаете сложный тип данных и хотите, чтобы ему можно было непосредственно присваивать значения типа Integer или Long (явно или косвенно), сделать это вам не удастся. Вместо этого следует определить Shared-функцию с именем FromXxx (исходный тип).

Пример:

 Shared Function FromLong(ByVal 1 As Long) As YourStructureType

Операторы

Многие изменения в операторах не требуют подробных объяснений и достаточно хорошо описываются в разделе «Language Changes» документации Visual Studio .NET.

Операторы AndAlso и OrElse

Как упоминалось выше в этой главе, в VB .NET операторы And и Or работают так же, как они работали в VB6. В дополнение к ним Microsoft определила два новых оператора, работающих только с логическими величинами: AndAlso и OrElse. Использование этих операторов продемонстрировано в листинге 8.7.

Листинг 8.7. Операторы AndAlso и OrElse

' Комбинированные логические операторы

' Copyright ©2001 by Desaware Inc. All Rights Reserved

Module Modulel

Public Readonly Property IsTrueO As Boolean 

Get

Console.WriteLine ("IsTrue was called") 

Return True

 End Get

 End Property

Public Readonly Property IsFalse() As Boolean

 Get

Console.WriteLine ("IsFalse was called")

 Return False

 End Get 

End Property

End

Sub Main()

Dim testvar As New A()

Console.WriteLine ("Before IsFalse And IsFalse")

 If testvar.IsFalse And testvar . IsFalse Then 

End If

Console.WriteLine ("Before IsFalse AndAlso IsFalse")

 If testvar.IsFalse AndAlso testvar.IsFalse Then 

End If

Console.WriteLine ("Before IsTrue Or IsTrue") 

If testvar.IsTrue Or testvar.IsTrue Then 

End If

Console.WriteLine ("Before IsTrue OrElse IsTrue")

 If testvar.IsTrue OrElse testvar.IsTrue Then

 End If

Console. ReadLine() 

End Sub

End Module

Программа выводит следующий результат:

Before IsFalse And IsFalse

IsFalse was called

IsFalse was called

Before IsFalse AndAlso IsFalse

IsFalse was called

Before IsTrue Or IsTrue

IsTrue was called

IsTrue was called

Before IsTrue OrElse IsTrue

IsTrue was called

Как видите, операторы AndAlso и OrElse оптимизируют проверку логических условий. Если одно условие в связке And равно False, проверять остальные условия бессмысленно, поскольку результат заведомо равен False. Иногда эти операторы заметно улучшают быстродействие программы, особенно если в условиях используются свойства объектов, получение которых занимает относительно много времени.

Строковые операторы

Ранее в этой главе упоминалось о том, что в VB6 иногда выполнял автоматические преобразования типов, приводящие к непредвиденным результатам. Например, выражение 15 + "15" могло оказаться равным 30. Строгая проверка типов предотвращает автоматические преобразования такого рода, отдельный оператор конкатенации становится ненужным, поэтому программисты могут вернуться к более наглядному оператору «+». Ниже приведен фрагмент приложения Operators.

' Console.Wri.teLine(15 + "15" + "15") ' Ошибка компиляции

Console.WriteLine("Evil Typing")

Console.WriteLine(15.ToString + "15" + "15")

В результате выводится строка 151515.

Конечно, вы можете продолжать пользоваться оператором конкатенации &. При работе со строками следует помнить о некоторых тонкостях, связанных с подсчетом символов (хотя эта проблема не имеет прямого отношения к операторам, она нередко вызывает недоразумения).

Во встроенных командах VB .NET отсчет производится привычным способом, от 1 до длины строки. С другой стороны, класс .NET String (на котором основаны переменные String) считает символы в строках, начиная с нуля. Так, фрагмент приложения Operators

Dim A As String = "ABCD"

Console.WriteLine ("String Characters")

Console.WriteLine (InStr(A, "C"))

Console.WriteLine (A.IndexOf("C"))

выводит следующий результат:

String Characters

3 .

2

Комбинированные операторы

В VB .NET поддерживаются новые комбинированные операторы, хорошо знакомые программистам C++. Комбинированный оператор выполняет с переменной некоторую операцию и присваивает результат исходной переменной. Например, команда А = А + В заменяется командой А += В.

В листинге 8.8 приведена функция Concatonators из приложения Operators.

Листинг 8.8. Комбинированные операторы

Sub Concatonators()

Dim S As String

Dim A As Integer

S = "Hello"

5 +=•" Everybody"

A = 5

A += 10

Console.WriteLine ("Concatonators")

Console.WriteLine (S)

Console.WriteLine (A) End Sub

Выводится следующий результат:

Concatonators 

Hello Everybody

 15

Новые операторы перечислены в табл. 8.2.

Таблица 8.2. Комбинированные операторы в VB .NET

А&= В

А = А & В (конкатенация строк)

А*=В

А- А*В

А+=В

А = А + В

А/= В

А= А/В

А-=В

А = А-В

А\=В

А= А\В

А^=В

А = А^В

Комбинированные операторы делают программу более наглядной и лаконичной, поэтому я рекомендую пользоваться ими.

 

Eqv и Imp

Оператор VB6 Eqv заменен оператором =, который работает точно так же. Оператор VB6 Imp заменяется следующей конструкцией: 

(Not A) Or В ' В логических операциях

 

Итоги

В этой главе было показано, что переход на архитектуру .NET потребовал значительных изменений в фундаментальных типах данных VB .NET и принципах их работы. Чтобы VB .NET был CLS-совместимым языком, его типы данных должны быть основаны на типах данных .NET.

Важнейшим изменением для большинства разработчиков VB станет появление жесткой проверки типов. Поведение VB .NET при отключенной проверке типов в этой главе не рассматривается — тот, кто сознательно отключает ее, слишком глуп для чтения этой книги1. Не стоит и говорить о том, что флажок Option Explicit тоже должен быть установлен.

При внесении столь фундаментальных изменений проектировщики Microsoft воспользовались случаем и подправили синтаксис языка. В частности, в новой версии поддерживается инициализация при объявлении — огромное усовершенствование, особенно при инициализации массивов, столь неудобной в VB6. Среди приятных нововведений стоит отметить и комбинированные операторы, хорошо знакомые программистам C++ и С#.

1 Наверное, я выразился слишком сильно (а может быть даже оскорбительно, чего я стараюсь избегать) — но, черт возьми, жесткая проверка тиной действительно важна! С позиции общих затрат на разработку программного продукта она легко выходит на первое место среди всех новшеств VB .NET. Итак, если в вашей программе проверка типов отключена, поскорее включите ее и исправьте ошибки (о которых вы наверняка и не подозревали)!

Назад   Вперёд

 



Заказ программ!
Вы можете заказать у меня написание необходимой вам программы. Чем популярнее будет она, тем меньше стоит работа.
Инфо
Сайт создан: 3 февраля 2000 г.
Рейтинг@Mail.ru
Главная страница