Visual Basic 2005 "Whidbey".
Новые синтаксические конструкции.
Автор: Андрей Щёкин
[darXeth]
Handy Labs
Опубликовано: 06.12.2002
Версия текста: 1.1
0. Введение
Синтаксис любого языка программирования, так или иначе, изменяется со
временем. У этих изменений может быть много разных причин. Иногда это просто
упрощения стандартных для этого языка действий. Иногда изменения в синтаксисе
соответствуют появлению новых концепций в языке или в его целевой платформе.
Потеря совместимости, которая произошла при переходе от Visual Basic 6.0 к
.Net - очень редкий случай. Обычно эволюция языка не содержит настолько резких,
'революционных' переходов. Это логично - никто не стал бы писать на языке,
который с каждой новой версией надо учить заново .
Переход с VB6 на VB .Net всё же, на мой взгляд, был оправдан. Он
соответствовал переключению интересов Microsoft с COM на .Net. Visual Basic был
очень тесно связан с COM (вся объектная модель VB, была, по сути, моделью COM).
Поэтому тупик технологии мог бы привести к тупику языка. Переключившись на .Net,
язык получил новые перспективы. Более того, расширение объектно-ориентированных
возможностей VB поставило его в один ряд с современными языками - Java и C#.
Переход на следующую версию Visual Basic - Visual Basic 2005, более известную
под кодовым названием 'Whidbey', произойдёт уже в рамках платформы .Net. Поэтому
все добавления в язык нисколько не затрагивают его обратную совместимость с VB
.Net, и любой проект VB .Net будет без изменений работать в Whidbey.
В этой статье я подробно расскажу о некоторых интересных дополнениях, которые
появятся в VB 2005. Эта статья - не перевод и не пересказ какого-либо
конкретного источника. Это компиляция того, что я узнал из блогов разработчиков
VB Whidbey и презентаций, доступных на сайте Microsoft.
Я надеюсь, что эта информация окажется интересной и тем людям, которые уже
представляют себе новые возможности 'Whidbey'. Я постарался дать более или менее
подробное описание того, "как это работает", да и сам синтаксис VB для этих
расширений может быть интересен (обычно в большинстве примеров приводится
синтаксис C#).
ПРИМЕЧАНИЕ
Когда я начинал писать эту статью, я собирался подробно разобрать
все новшества VB 2005. Но, написав первые четыре части, я понял,
что это куда более сложная задача, чем сперва казалось. В первую очередь –
из-за большого количества менее значительных дополнений. Во-вторых – из-за
постоянно появляющейся новой информации. Поэтому я остановлюсь только на
части новых конструкций, предоставив рассмотреть оставшиеся новшества
другим авторам. |
1. Обобщённые типы (Generic types)
Причина возникновения
Новая концепция в .Net Framework 1.2.
Синтаксис
Dim Users As List(Of User)
Public Sub Swap(Of T) (ByRef value1 As T, ByRef value2 As T)
Dim swapValue As T
swapValue = value1
value1 = value2
value2 = swapValue
End Sub
Public Class SortedList(Of T) Where T As IComparable
...
End Class
|
Основы
Обобщённые типы вскоре появятся в Java и, чуть позже, займут своё место и в
.Net Framework. Их поддержка, пожалуй, является наиболее существенным
добавлением к .Net Framework "Whidbey" по сравнению с предыдущими версиями.
Что такое "обобщёные типы"? Если говорить формально - это типы, которые могут
принимать другие типы в качестве параметров. Классическим примером здесь
является список (ArrayList). Когда мы создаём список, мы обычно собираемся
хранить в нём элементы конкретного типа. С другой стороны, нам нужно либо
написать отдельный список для каждого типа элементов, либо исходная его
реализация должна работать для любого типа. В текущем .Net Framework обобщение
класса ArrayList на любой тип элементов основано на возможности приведения
любого типа к базовому типу Object. Можно увидеть, что методы и свойства класса
ArrayList работают с Object:
Public Class ArrayList
...
Public Overridable Function Add(ByVal value As Object) As Integer
Public Overridable Sub Remove(ByVal obj As Object)
Public Overridable Overloads Function ToArray() As Object()
Default Public Overridable Property Item(ByVal index As Integer) As Object
...
End Class
|
Недостатки подобного подхода очевидны. Во-первых, мы легко можем добавить в
список элементы, не имеющие к нему ни малейшего отношения:
Dim Users As New ArrayList
With Users
.Add(Administrator)
.Add(Guest)
.Add(5)
End With
|
Во-вторых, использование любого элемента списка требует приведения типов:
CType(Users(0), User).Login
|
В-третих, самое неприятное, добавление значащих типов (value types) в список
требует их приведения к ссылочному типу, упаковки (boxing):
Работа со ссылочным типом (reference type) вместо значащего и сама упаковка
приводят к заметному снижению производительности.
ПРИМЕЧАНИЕ
В Java стандартные типы (int, bool, etc) не наследуют от Object.
Поэтому Java использует специальные объектные типы-обёртки - Integer,
Boolean, ... для добавления стандартных типов в коллекции. Проблемы с
производительностью тут ещё более наглядны. |
Стандартное решение первых двух проблем - написать свою коллекцию или список.
Некоторые полезные классы (вроде CollectionBase ) позволяют сделать это не
слишком сложным способом. Но писать свою коллекцию, которая будет использоваться
всего пару раз внутри проекта - достаточно неприятная задача. Кроме того, для
решения вопроса с упаковкой придётся реализовывать хранение элементов полностью
вручную.
Подобные задачи встречаются очень часто, и хотелось бы получить возможность
просто и быстро решать их. Идеальным было бы указать компилятору, какого типа
элементы будет храниться в данной коллекции, не задумываясь о её внутреннем
устройстве. Если мы оставим проверку типов среде разработки, а оптимизацию -
компилятору, освободившееся время можно будет потратить на более важные
вещи.
Суть обобщённых типов - именно в таком упрощении. При любом конкретном
применении обобщённого типа мы должны указать один или несколько
типов-параметров. Эти параметры затем будут использованы для определения
внутренней структуры обобщенного типа. Пример: в новом пространстве имён
System.Collections.Generics будет класс List(Of T) - обобщенный список.
Public Class List(Of T)
...
Public Overridable Function Add(ByVal value As T) As Integer
Public Overridable Sub Remove(ByVal obj As T)
Public Overridable Overloads Function ToArray() As T()
Default Public Overridable Property Item(ByVal index As Integer) As T
...
End Class
|
T здесь - тип-параметр. Теперь мы можем объявить список с любым нужным нам
типом элементов. Все наши проблемы с учётом этого способа решаются заметно
проще:
Dim Users As New List(Of
User)
With Users
.Add(Administrator)
.Add(Guest)
.Add(5)
End With
Users(0).Login
|
И в примере с числами никакой упаковки происходить не будет - параметр будет
передан по значению в соответствии с типом списка.
Дополнительно
Кроме обобщённых типов, в Visual Basic 2005 появятся и обобщенные функции.
Представим себе функцию, сортирующую массив:
Версия Visual Basic
.Net
Public Class Array
...
Public Shared Sub Sort(ByVal array As Array)
End Sub
...
End Class
|
Версия Visual Basic
2005
Public Class Array
...
Public Shared Sub Sort(Of T)(ByVal array As T())
End Sub
...
End Class
|
Второй подход очевидно быстрее для случая значимых типов. Но самое интересное
состоит в том, что мы вовсе не обязаны при вызове функции Sort указывать T явно.
Если написать просто:
Dim a() As Integer = {1, 8,
7, 5, 9}
Array.Sort(a)
|
нужный тип-параметр T будет подобран автоматически, на основе типа аргумента
a .
Кроме того, обобщенными могут быть и объявления делегатов, что избавляет нас,
например, от необходимости создавать свои процедуры обработчиков событий
(EventHandler). Поскольку все обработчики событий должны отличаться только типом
второго аргумента, можно просто задать этот тип в качестве параметра.
Public Sub EventHandler(Of T
As EventArgs) (ByVal sender As Object, ByVal e As T)
|
Этот пример демонстрирует ещё одно важное свойство обобщенных типов – способ
задавать ограничения на типы-параметры. Таким образом мы задаём интерфейс или
базовый тип для типа-параметра. Ограничения необходимы для того, чтобы мы могли
точно знать, что объекты этого типа поддерживают необходимые нам
свойства/методы. Для класса SortedList, например, тип элементов должен как
минимум реализовывать интерфейс IComparable.
Кроме того, возможно
использование специального ограничения – New (пример взят из статьи [5]):
Class Factory(Of T As New)
Function CreateInstance() As T
Dim NewInstance As T = New T
...
Return NewInstance
End Function
End Class
|
Это ограничение определяет, что тип-параметр поддерживает создание своих
экземпляров с помощью стандартного конструктора. Этим способом в приведённом
примере исключаются интерфейсы, модули и типы с модификатором MustInherit, а так
же все остальные классы, для которых не определён стандартный конструктор (без
аргументов).
Ещё одним интересным добавлением к .Net Framework, связанным с обобщёнными
типами, станет тип Nullable. Использование этого типа позволяет присваивать
Nothing значимым типам (структурам и базовым типам вроде Integer или Boolean).
Для этого вместо Dim a As Integer достаточно будет написать Dim a As Nullable(Of
Integer). После этого кроме обычных значений типа Integer, для a будет доступно
значение Nothing.
Такие типы крайне полезны при работе с базами данных, где часть полей записи
может иметь значение NULL. К сожалению, следующая версия Visual Basic в
поддержке Nullable будет заметно уступать C#.
ПРИМЕЧАНИЕ
В C# для Nullable введен специальный синтаксис: Nullable a
можно записать как int? a эквивалентно. Кроме этого, C# будет поддерживать
специальный оператор ??, позволяющий преобразовать значение типа Nullable
в значение обычного типа (int? x; int y; y = x ?? -1;). Также будут
позволено применение операторов к величинам типа Nullable и их
преобразование из одного подтипа Nullable в другой. То есть, если для типа
int определено сложение и преобразование в long, то для типа int? эти
операции тоже будут работать, возвращая, соответственно, int? и
long?. |
В VB 2005 мы, скорее всего, ещё не сможем применять операторы к объектам типа
Nullable и проводить их преобразования в другие типы. Тип Nullable был добавлен
к .Net Framework Whidbey слишком поздно, поэтому команда разработчиков VB не
успевает реализовать эти дополнительные возможности. Тем не менее, их реализация
уже запланирована на следующую версию языка.
Как это работает
Just-In-Time (JIT) компилятор во время исполнения создаёт отдельный тип для
каждой версии обобщённого типа, использованной в программе (List(Of Integer),
List(Of String), List(Of User)). Таким образом, каждый из этих классов
оказывается оптимизирован под соответствующий тип данных.
Поддержка обобщённых типов на уровне IL-ассемблера и метаданных позволяет
экспортировать их динамически подключаемых сборок. Возможность, казалось бы,
очевидная для программиста .Net. Но в этом обобщённые типы серьёзно отличаются
от шаблонов (templates) из C++, построенных на похожей идее, но поддерживаюшихся
только на уровне компилятора.
Моё мнение
Обобщенные типы - концепция, которая очень хорошо укладывается в общую
объектно-ориентированную направленность современных языков. Они позволяют решить
многие стандартные проблемы столь просто и удобно, что я с трудом понимаю, как
мы могли без них обходиться. Очень жаль, что они не появились ещё в Framework
версии 1.0.
2. Перегрузка операторов (operator overloading)
Причина возникновения
Перегрузка операторов стоит на первом месте в списке требований программистов
к разработчикам языка Visual Basic.
Синтаксис
Public Structure Complex
...
Public Shared Operator+ (ByVal c1 As Complex, ByVal c2 As Complex) As Complex
Return New Complex(c1.Re + c2.Re, c1.Im + c2.Im)
End Operator
Public Shared Operator- (ByVal c1 As Complex, ByVal c2 As Complex) As Complex
Return New Complex(c1.Re - c2.Re, c1.Im - c2.Im)
End Operator
Public Shared Widening Operator CType
(ByVal value As Double) As Complex
Return New Complex(Re:=value, Im:=0)
End Operator
...
End Structure
|
Основы
Перегрузка операторов - концепция очень известная. Она поддерживается многими
современными языками (можно сразу назвать C++, C# и Delphi). Поддержка
перегрузки операторов в VB планировалась ещё в версии .Net, но реально появится
только в VB 2005.
Чем это может быть полезно? Самый простой и наглядный пример - типы Date
(DateTime) и TimeSpan. Visual Basic поддерживает DateTime из .Net Framework как
базовый тип - Date. Поскольку этот тип - базовый, VB понимает любые операции
сравнения с его использованием:
Public Function IsOverdue(Date date) As Boolean
Return date > Date.Now
End Function
|
Но с типом TimeSpan всё сложнее. Чтобы сравнить две переменные этого типа,
нужно использовать определённые в нём функции Compare или CompareTo. В итоге
получаются неудобные конструкции вроде
If timeSpan1.CompareTo(timeSpan2) > 0 Then Me.DoSomewhat()
|
Этот код намного сложнее понять.
Однако программисты на C# с самой первой версии языка могут проводить это
сравнение таким образом: if(timeSpan1 > timeSpan2). Более того, они могут
вычитать и складывать промежутки времени обычными операторами '+' и '-', хотя
TimeSpan - вовсе не базовый тип языка C#.
Весь секрет - в перегрузке операторов. Используя её, класс может
переопределить ("перегрузить") поведение стандартных операторов, применённых к
его экземплярам. Обычно переопределяются арифметические операторы, операторы
сравнения и операторы приведения типов.
ПРИМЕЧАНИЕ
C++ поддерживает переопределение оператора присвоения и оператора
обращения к методу класса, но это заметно усложняет язык. Кроме того,
реализация этих операторов в VB .Net в любом случае была бы невозможна
из-за используемой в .Net системы работы с
памятью. |
Переопределение операторов в VB 2005 будет выглядеть примерно так:
Public Class TimeSpan
...
Public Shared Operator> (ByVal t1 As TimeSpan, ByVal t2 As TimeSpan) As Boolean
Return t1.Ticks > t2.Ticks
End Operator
Public Shared Operator< (ByVal t1 As TimeSpan, ByVal t2 As TimeSpan) As Boolean
Return t1.Ticks < t2.Ticks
End Operator
...
End Class
|
На самом деле, для C# определение класса TimeSpan выглядит так уже сейчас.
Но, начиная с VB 2005, этим определением смогут воспользоваться и программисты
VB.
Приведение типов - ещё один вид перегруженных операторов. Он очень полезен
для классов, расширяющих возможности базовых (да и любых закрытых) типов. Пусть,
например, мы хотим написать свой класс AdvancedString, который будет
поддерживать некоторые дополнительные функции работы со строками. Унаследовать
его от стандартного типа String мы не сможем (это запрещено по соображениям
безопасности).
Логичная мысль - положить строку во внутреннее поле нашего класса и затем уже
применять ней новые методы. У этого решения есть один недостаток - при любом
обращении к этой внутренней строке снаружи придётся явно указывать
соответствующее свойство класса AdvancedString. Это неудобно, так как может
получиться выражение вида:
str4 = New AdvancedString(str1.InnerString & str2.InnerString & _
str2.InnerString.Substring(3, 5))
|
Можно, конечно, переопределить все операторы (и & в том числе), но всё
равно останется необходимость явно вызывать конструктор. Для того, чтобы от него
избавиться, нужно определить в нашем классе операторы приведения типов (в строку
и из строки):
Public Class AdvancedString
...
Public Shared Narrowing Operator CType(ByVal value As AdvancedString) As String
Return value.InnerString
End Operator
Public Shared Widening Operator CType(ByVal value As String) As AdvancedString
Return New AdvancedString(value)
End Operator
...
End Class
...
str4 = str1 & str2 & CStr(str2).Substring(3, 5)
|
Приведение типов уже поддерживается, например, классом IntPtr. Для приведения
указателя к целому типу в VB мы используем в настоящее время методы ToInt32 и
ToInt64. Но в C# вместо вызова этих методов можно использовать стандартные
операторы приведения (int) или (long).
Ещё некоторые очевидные применения перегрузки операторов - создание
математических классов, вроде векторов или комплексных чисел.
Дополнительно
Список операторов, поддерживающих перегрузку в Visual Basic 2005:
+ |
+ (унарный) |
- |
- (унарный) |
* |
\ |
/ |
^ |
& |
Like |
Mod |
And |
Or |
Xor |
>> |
<< |
Not |
CType |
= (только сравнение) |
<> |
< |
> |
<= |
>= |
IsTrue |
IsFalse |
Тут всё должно быть понятно, кроме IsTrue и IsFalse. Эти два оператора
используются в логических конструкциях AndAlso и OrElse. Так как в выражении a
OrElse b обращение к b происходит не всегда, необходим способ независимо
определить логическое значение a.
Кроме того, раньше (в одном из примеров) я употребил перед оператором CType
специальное ключевое слово Narrowing. Такой синтаксис действительно встречается
в примерах от Microsoft, но там он почти не пояснён.
К счастью, поскольку перегрузка операторов уже присутствует в C#, можно
искать аналоги в этом языке. Как выясняется, модификатор Narrowing - прямой
аналог модификатора C# explicit. С помощью Narrowing мы определяем, что
применение соответствующего преобразования типов приводит к потере информации.
Примером может быть преобразование Double в Integer.
В режиме Option Strict (а разумный программист должен находиться в этом
режиме всегда), такое преобразование будет требовать явного применения
оператора CType.
Как это работает
На самом деле, перегруженные операторы - это обыкновенные функции. При
перегрузке, скажем, оператора '+' IL-компилятор создаёт в нашем классе или
структуре функцию с названием op_Addition, принимающую те же два слагаемых (для
оператора '-' - op_Substraction, и так далее). В дальнейшем на место
использованных операторов подставляются вызовы соответствующих функций.
Но если мы сейчас напишем на VB функцию с названием op_Addition, C# не поймёт
её как оператор. Почему? На этот вопрос есть простой ответ. Вот объявление
функции op_Addition для класса TimeSpan на IL-аcсемблере:
.method public hidebysig
specialname static
valuetype System.TimeSpan op_Addition(valuetype
System.TimeSpan t1,
valuetype System.TimeSpan t2) cil managed
|
Здесь наиболее интересен атрибут specialname. Обычным способом установить его
для нашей функции не удастся, поэтому она и не будет распознана как настоящий
оператор.
Сложно сказать, правилен ли такой подход - на мой взгляд, Microsoft стоило
дать нам устанавливать этот атрибут вручную. Но, в любом случае, в Visual Basic
2005 создание подобных специальных функций наконец станет выполнимым.
Моё мнение
Перегрузка операторов может быть весьма полезна, если подходить ней разумно.
С другой стороны, применённая неверно, эта возможность позволяет создать весьма
запутанный и сложный для понимания код. Представьте себе "простое" присвоение a
= b, кидающее исключение из оператора CType, определённого в a!
ПРИМЕЧАНИЕ
Java не поддерживает (и не будет поддерживать в обозримом будущем)
перегрузку операторов из схожих соображений. |
Но я сам скорее рад появлению перегрузки операторов. Перегрузить Like для
строк на поддержку регулярных выражений было бы занятно.
3. Беззнаковые типы (unsigned types)
Причина возникновения
Поддержка таких типов в C#.
Синтаксис
Dim u As UInteger = 100000UI
Dim s As SByte = -125
Dim sum As ULong = u + s
|
Основы
Беззнаковые типы не являются важной или значимой особенностью языка, но, тем
не менее, присутствуют во многих языках. Сама платформа .Net тоже изначально
поддерживала беззнаковые типы (UInt16, UInt32, SByte, ...).
Но хотя в VB .Net и позволялось объявлять переменные этих типов, до сих пор с
ними было невозможно выполнять никаких полезных действий. В C#, однако, можно
свободно использовать такие переменные. Этот эффект достигается не
переопределением операторов внутри типа UInt32 или SByte, а именно поддержкой
этих типов на уровне языка.
С выходом Visual Basic 2005 ситуация изменится. Добавление новых беззнаковых
типов (вроде UInteger), полностью поддерживаемых языком, позволит работать с
ними так же прозрачно, как с обычными числовыми типами. Это должно облегчить
взаимодействие с языком C#, и упростить перенос кода между VB .Net и C#. Кроме
того, это должно облегчить поиск ошибок в тех случаях, когда переменные (или
аргументы процедуры) не должны принимать отрицательных значений.
Новые суффиксы для числовых значений US, UI, UL (для UInteger, UShort и ULong
соответственно) позволят помечать числовые значения как беззнаковые:
Dim a As UInteger = 5UI
Dim b As UInteger = -5UI
|
Дополнительно
Беззнаковые типы, поддерживающиеся в Visual Basic 2005:
название: |
диапазон значений: |
аналог: |
UShort |
0 .. 65 536 |
Short |
UInteger |
0 .. 4 294 967 295 |
Integer |
ULong |
0 .. 1.84467E+19 |
Long |
SByte |
-127 .. 127 |
Byte |
ПРИМЕЧАНИЕ
Название "беззнаковые типы" не очень верно, на самом деле. Среди новых
типов в Visual Basic 2005 будет добавлен тип SByte, который является как
раз знаковым эквивалентом стандартного типа
Byte. |
Одной из интересных особенностей беззнаковых типов является возможность
перегружать функции по типу аргумента, скажем, Integer или UInteger. При вызове
такой функции нужный её вариант будет вызван в зависимости от наличия знака у
аргумента.
Важное замечание - беззнаковые типы не совместимы с CLS (Common Language
Specification). Это значит, что многие языки .Net не смогут использовать методы,
принимающие аргументы беззнаковых типов (или возвращающие такие значения).
Собственно, сам Visual Basic в текущей версии не может их использовать. Это
означает, что при разработке библиотек классов необходимо представлять кроме
таких методов ещё и их перегруженные версии, принимающие обычные знаковые
типы.
Как это работает
Беззнаковые типы по своему устройству ничем не отличаются от знаковых. В
памяти тип Integer хранится как четыре последовательных байта (32 бита). Его
аналог, UInteger - хранится совершенно идентично. Более того, процессорные
операции сложения и вычитания не зависят от наличия у числа знака ( -1 =
&HFFFFFF, добавив единицу - получаем ноль за счёт переполнения).
Различие на уровне языка состоит только в том, как интерпретировать эту
последовательность байт. Для типа Integer первый бит может рассматриваться как
знак. Если он равен 0, то значение положительное, 1 - отрицательное. Остальные
биты тогда определяют само число. В случае UInteger первый бит - часть
беззнакового числа, что позволяет в два раза увеличить его максимальный
размер.
Моё мнение
Мне кажется, что это новое дополнение по большей части бесполезно, и только
вносит излишнюю путаницу в язык, не добавляя при этом ничего стоящего.
Достоинством VB всегда была простая система целочисленных типов (реально
использовались только Byte и Integer (в VB6 - Long)). Добавление новых типов
заметно усложняет выбор.
Опять же, несовместимость беззнаковых типов с CLS заставляет меня задуматься
о том, стоит ли их использовать вообще. Потенциальная несовместимость кода -
достаточно серьёзный аргумент против этого.
4. Блок Using.
Причина возникновения
Поддержка этой конструкции в C#.
Синтаксис
Using writer As New
StreamWriter("somefile.txt")
writer.WriteLine("Начало действия.")
writer.WriteLine("Действие завершено.")
End Using
|
Основы
Ключевое слово Using пришло в Visual Basic из C#.
ПРИМЕЧАНИЕ
В C# эта идея появилась из C++, в которой подобные конструкции часто и
реализовывались через шаблонные классы (вроде auto_ptr).
|
Значение Using непосредственно связано с интерфейсом IDisposable, одним из
наиболее часто используемых в .Net Framework интерфейсов. Интерфейс IDisposable
представляет нам возможность быстро освободить общие ресурсы, не полагаясь на
автоматический сборщик мусора (garbage collector). Реализуя этот интерфейс,
класс реализует процедуру Dispose, в которой и происходит освобождение
ресурсов.
Стандартные применения IDisposable не ограничиваются освобождением ресурсов.
Для объектов вроде файлов и потоков вызов Dispose обычно эквивалентен закрытию
объекта методом Close.
Но если забыть сделать Dispose для таких объектов, это может иметь
непредвиденные последствия. Если ресурсы обычно освобождаются в методе Finalize,
то класс StreamWriter, например, не вызывает Close из этого метода. Это, как мне
кажется, сделано для возможности подключить к одному потоку несколько объектов
StreamWriter.
Конструкция Using позволяет вызывать метод Dispose автоматически, как только
нужный объект выйдет за блок Using.
Простой пример:
Dim contents As String
Using reader As New StreamReader("somefile.txt")
contents = reader.ReadToEnd();
End Using
|
Как это работает
Блок Using относится к тому же типу, что и блок For Each. Оба этих ключевых
слова не связаны с конкретными командами IL-ассемблера. Вместо этого, компилятор
просто преобразует их в другие, чуть более длинные конструкции (скорее всего
даже ещё перед компиляцией).
Дизассемблировав результат, легко увидеть конструкцию, эквивалентную
использованию Using:
Используя блок Using
Using resource As New
MyResource
resource.UseSomehow()
End Using
|
Аналогичный эффект без
Using
Dim resource As New
MyResource
Try
resource.UseSomehow()
Finally
If Not resource Is Nothing Then
CType(resource, IDisposable).Dispose()
End If
End Try
|
Видно, что здесь есть не только вызов Dispose для нашего объекта, но и
гарантия того, что он будет вызван даже в случае исключения. Кстати, вызов
Dispose в случае исключения может привести к ошибочному состоянию объекта на
момент вызова. Это, естественно, приходится учитывать при реализации этого
метода.
Если тип нашего объекта - не класс, а структура, то в сгенерированном коде
отсутствует, по понятным причинам, проверка на Nothing.
Моё мнение
Подобные синтаксические 'сокращения' кажутся мне очень удачной идеей,
поскольку при всех их достоинствах совершенно не требуют дополнительной
поддержки самой .Net Framework. И если в случае с For Each приходится учитывать
возможную потерю производительности, то здесь этой проблемы не возникает.
Опять-таки, жаль, что мы не получили поддержки Using ещё в VB .Net.
5. Ключевые слова TryCast, IsNot, Continue.
Причина возникновения
Поддержка аналогичных ключевых слов в C#, соображения удобства.
Описание
Ещё три новых ключевых слова, ожидаемых в Visual Basic 2005, соответствуют
удобным возможностям языка C#, до сих пор в VB не поддерживавшимся.
Самое простое из новых ключевых слов - IsNot. Это оператор, обратный
оператору Is, смысл которого можно продемонстрировать на следующем простом
примере:
Версия Visual Basic
.Net
If Not obj Is Nothing Then
Else
End If
|
Версия Visual Basic
2005
If obj IsNot Nothing Then
Else
End If
|
Оператор TryCast чуть менее очевиден. TryCast очень похож своей логикой на
другие операторы приведения типов - CType и DirectCast. Но если приведение к
заданному типу невозможно - он просто возвращает Nothing вместо генерации
исключения.
У выражения с TryCast тоже есть своё соответствие в VB .Net:
Версия Visual Basic
.Net
Dim req As WebRequest = WebRequest.Create(uri)
If TypeOf req Is HttpWebRequest Then
Dim httpreq As HttpWebRequest = DirectCast(req, HttpWebRequest)
httpreq.AddRange();
...
Else
Throw New NotSupportedException
End If
|
Версия Visual Basic
2005
Dim req As WebRequest = WebRequest.Create(uri)
Dim httpreq As HttpWebRequest = TryCast(req, HttpWebRequest)
If httpreq IsNot Nothing Then
httpreq.AddRange();
...
Else
Throw New NotSupportedException
End If
|
Казалось бы, принципиальной разницы между приведёнными примерами нет - в
случае ошибки и там и там возникнет исключение. Если мы посмотрим на
IL-ассемблер, то оператору DirectCast в нём будет соответствовать команда
casttype. Эта команда, как можно догадаться, приводит тип объекта на верхушке
стека к указанному типу и генерирует исключение, если такое приведение
невозможно. Оператору TryCast будет соответствовать команда isinst, которая
полностью аналогична casttype, но в случае неудачи просто помещает в стек null
(Nothing).
Но, как выясняется, здесь есть одно значительное различие. В IL коде оператор
TypeOf тоже выглядит как isinst плюс проверка на null (Nothing). То есть, любой
вызов TypeOf приводит к преобразованию типа (или попытке преобразования),
результат которого никогда не используется! Вызов CType или DirectCast после
TypeOf гарантирует нам две операции приведения типа вместо одной.
Для решения этой проблемы и был создан TryCast, который
позволяет оптимизировать количество операций приведения типов в коде. На
мой взгляд, этот оператор очень полезен пониманием того, что всё же происходит в
очевидном на первый взгляд коде.
Третьей, и самой полезной из новых ключевых слов является команда Continue.
Эта команда позволяет немедленно перейти к следующей итерации цикла. До сих пор
такую возможность (с самого начала существовавшую в C#) в VB .Net можно было
эмулировать только командой GoTo. Понятно, что такое решение нельзя
рассматривать серьёзно.
Появление Continue позволит заметно упростить и оптимизировать некоторые
часто встречающиеся конструкции. Хорошим примером может быть применение
какой-либо операции к тем элементам массива, которые удовлетворяют заданному
условию:
Версия Visual Basic .Net
For Each contact As Contact
In contacts
If contact.MailAddress.Length > 0 AndAlso Not contact Is myself Then
Dim message As New MailMessage()
With message
.To = contact.MailAddress
End With
SmtpMail.Send(message)
End If
Next contact
|
Версия Visual Basic
2005
For Each contact As Contact
In contacts
If contact Is myself Then Continue For
If contact.MailAddress.Length = 0 Then Continue For
Dim message As New MailMessage()
With message
.To = contact.MailAddress
End With
SmtpMail.Send(message)
Next contact
|
Если операции, выполняемые в цикле, сами по себе имеют значительную
вложенность, то, избавляясь от одного уровня, мы заметно улучшаем читаемость
кода. Кроме того, команда Continue может быть использована и для перехода к
следующей итерации внешнего цикла, поскольку она, как и команда Exit,
поддерживает непомредственное указание типа цикла.
Моё мнение
Как я уже говорил, самым полезным и незаменимым из добавленных ключевых слов,
на мой взгляд, является команда Continue. Когда я начинал писать эту главу, я не
был ещё уверен в полезности оператора TryCast. Но, как видно, в силу
особенностей языка MSIL наличие в VB 2005 TryCast заметно полезнее наличия в нём
TypeOf.
6. Заключение
В этой статье я рассмотрел важные дополнения к языку Visual Basic в его
следующей версии, и постаралася предоставить наиболее полную информацию по
каждой из новых конструкций. Тем не менее, большинство из этих конструкций
заслуживает отдельных статей, и, как я надеюсь, такие статьи ещё появятся.
Кроме того, я оставил нерассмотренными такие важные дополнения к языку, как
определение раздельного доступа к свойствам, полное описание делегатов и
пространство имён My. Я не стал приводить синтаксис для этих конструкций, но я
надеюсь, что через некоторое время они тоже будут описаны подробно.
Как можно видеть из уже изученных дополнений – эволюция языка Visual Basic
продолжается, и с каждым шагом мы получаем всё более мощный язык, который при
правильном использовании позволяет просто и быстро решить почти любую задачу.
Единственный вопрос - стоят ли новые возможности всё большего усложнения языка?
Как мне кажется, по этому поводу возникнет ещё немало споров. На мой взгляд -
стоят.
[eiiiaioe?iaaou
yoo noaou?]
Источники
- Paul Vick (Panopticon Central)
- Cameron Beccario (http://weblogs.asp.net/cambecc/))
- Bill McCarthny ()
- Language
Enhancements in Visual Basic Whidbey
- Defining
and Using Generics in Visual Basic 2005
Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен
в какой бы то ни было форме и какими бы то ни было средствами без письменного
разрешения владельцев авторских прав.