А ти готовий до Visual Basic.NET? – II, VB.NET, ASP, статті

  Поради тим, хто збирається працювати з новою версією Visual Basic (Продовження;
початок см. в Byte / Росія № 3/2001).

У світовому (в першу чергу американському) співтоваристві VB-програмістів тривають досить емоційні суперечки навколо майбутньої версії Visual Basic. За оцінками експертів журналу Visual Basic Programmer “s Journal, більшість розробників схвалюють технологію. NET, але їх подяку корпорації Microsoft ніяк не можна назвати безмежною.

Проте можна констатувати, що багатомільйонна (за деякими оцінками, від 2 до 4 млн. чоловік) армія користувачів VB-інструменту розділилася на дві частини. Прихильники нововведень технології. NET (на чолі з Microsoft) наголошують на тому, що за допомогою нового Visual Basic вони отримають можливість створювати додатки масштабу підприємства, в тому числі Web-і серверні додатки. Противники (точніше, критики) говорять про серйозну загрозу стабільності величезної бази існуючого VB-коду. Власне, майже всі згодні з необхідністю реформування Visual Basic, але висловлюють наполегливі побажання забезпечити більш високу сумісність з існуючими версіями і розтягнути реалізацію всіх нововведень на кілька наступних версій пакету.

При цьому зрозуміло, що сяють дискусій визначається не емоціями, а чисто практичними інтересами – все розробники розуміють, що реалізація нововведень Visual Basic.NET може серйозно вплинути на їх особисту долю. Є побоювання, що перехід від нинішньої архітектури Windows до майбутньої. NET може виявитися настільки ж болючим, як перехід на початку 90-х від DOS до Windows: значна частина DOS-програмістів просто не змогла (в тому числі і чисто психологічно) адаптуватися до новим методам розробки.

Зараз ніхто не заперечує ймовірність того, що Microsoft піде на деякі поступки критикам, але всі впевнені, що корпорація буде просуватися по вибраному її керівництвом курсу, націленому, в Зокрема, на завоювання сегмента великих корпоративних клієнтів. За Принаймні, вся десятирічна історія Visual Basic показує, що за зовнішньої уважністю до запитів розробників лежить досить жорстка лінія стратегії Microsoft, яка веде співтовариство програмістів в потрібному корпорації напрямку.

Всі ми, звичайно, розуміємо, що за риторикою про “найбільш повному задоволенні потреб трудящих “ховається початкова мета будь-якого комерційного підприємства – збільшення доходів його акціонерів, причому в довгостроковій перспективі. Інша справа, що бізнес-успіх навряд чи можливий без урахування потреб ринку, але всім зрозуміло, що взаємини постачальників і покупців будуються на основі діалектичного закону єдності і боротьби протилежностей і виливаються в пошук компромісу між інтересами двох сторін.

А тепер перейдемо до порад …

Рада 18. Будьте уважні при визначенні масивів

При роботі з масивами VB-програмістів очікує цілий ряд сюрпризів, причому не дуже приємних. Загальні рекомендації такі: виключіть використання оператора Option Base 1 (його немає в Visual Basic.NET), застосовуйте масиви з нульовою нижньою межею індексу і переглянете варіанти динамічного визначення масивів.

Прихильники істинного Basic * можуть радіти – ми повертаємося в далеке минуле: тепер в “рідному” варіанті Visual Basic.NET підтримує тільки масиви з нульовою нижньою межею.


* До речі, в середині 80-х років Томас Курц, один з творців першого Basic (це було в 1964 р.), розробив систему, яку назвав True Basic. Тим самим він хотів підкреслити, що багато інших Basic-інструменти відійшли від початкових канонів цієї мови.

Але як раз тут програмістів чекає «підводний камінь» – одна і та ж конструкція в Visual Basic.NET і у всіх попередніх версіях буде працювати по-різному. Інакше кажучи, фрагмент коду

Dim Arr(4)
For i = LBound(Arr) To 4
Arr(i) = 0
Next

працював завжди бездоганно, в Visual Basic.NET видасть помилку при i = 4. Справа в тому, що визначення масиву:

Dim Arr(N)

у всіх версіях Basic інтерпретувалося як

Dim Arr (0|1 To N)

(0 | 1 – в залежності від оператора Option Base), тобто N позначало верхню межу індексу. Але в Visual Basic.NET це буде вказувати число елементів:

Dim Arr (0 To N-1)

Тут корисно нагадати, що в Visual Basic і раніше допускаються два формату опису розмірності масиву (на прикладі одновимірного):

Dim Arr(N)

і

Dim Arr(lowIndex To hightIndex)

Загальна рекомендація завжди була однакова – потрібно використовувати другу форму, щоб уникнути двозначності. Справа в тому, що в класичному, стародавньому Basic була тільки перша форма визначення індексу масиву, яка відповідала опису

Dim Arr(0 To N)

Потім, щоб забезпечити сумісність з FORTRAN (там нумерація починалася з 1), був введений керуючий оператор Option Base 0 | 1, який дозволяв встановлювати нижню межу з нуля або одиниці. Але це породило проблему невизначеності. Наприклад, при копіюванні фрагмента коду Dim Arr (N) в якийсь модуль конкретне значення нижньої межі масиву визначалося оператором Option Base саме цього модуля. В одному випадку це міг бути 0, в іншому – 1.

Таким чином, тільки явне завдання нижньої межі може усунути цю невизначеність та мінімізувати проблеми при переході в Visual Basic.NET. А ще краще, якщо при обробці масиву ви будете постійно контролювати поточні значення кордонів індексу. Такий код гарантує, що ви не вийдете за межі індексу:

For i = LBound(Arr) To UBound(Arr)
Arr(i) = 0
Next

Але складності при використанні ненульовий нижньої межі залишаться – всі такі масиви будуть перетворені в масиви типу Wrapper Class, тобто рядок

Dim a(1 To 10) As Integer

поміняється на

Dim a As Object = New VB6.Array(GetType(Short), 1, 10)

Проблема полягає в тому, що такі масиви працюють помітно повільніше в порівнянні з “рідними” і існують деякі обмеження на їх застосування. Наприклад, wrapper-масиви не можна передавати в C-класи та в процедури, які використовують параметри типу Array.

Що стосується динамічного визначення масиву, то тепер конструкція

Dim v ReDim v (10) "працює в Visual Basic 6.0

працювати не буде – мінлива відразу повинна бути визначена як масив:

Dim v(2) ReDim v (10) "працює в Visual Basic.NET

Рада 19. Відмовтеся від неявного перетворення типів даних

Цьому раді варто слідувати незалежно від того, чи збираєтеся ви переходити на Visual Basic.NET. В обгрунтування цієї тези можна привести багато прикладів, ми обмежимося двома. Так, деякі програмісти для управління станом прапорця замість конструкції:

Dim newDelete As Boolean
If newDelete Then
chkDeleteMe.Value = 1
Else
chkDeleteMe.Value = 0
End If

використовують більш короткий код:

chkDeleteMe.Value = -1 * newDelete

Тут треба мати на увазі кілька явних мінусів другого варіанту.

  1. Він здається більш коротким, але з точки зору створюваного машинного коду менш ефективний, по крайней мере, по швидкості виконання.
  2. Результат роботи цього коду неочевидний. Не впевнений, що будь VB-програміст з ходу відповість, який буде результат при DeleteMe =
    True.
  3. В Visual Basic.NET він не буде працювати (про це нижче).

Другий приклад пов’язаний з тим, що при використанні неявного перетворення типів даних зростає небезпека, що програма буде працювати зовсім не так, як бачилося її автору. Слід також враховувати особливості національних форматів представлення даних (для речових чисел і дат). Ось ще один приклад на цю тему, реалізований в Windows з російськими регіональними установками:

strR1$ = Str$(2.34)
strR2$ = 2.34 Print strR1 $, strR2 $ "буде надруковано 2.34 2,34

Як бачите, ми отримали два різних результату, хоча, здавалося б, вони повинні бути однакові. Тут слід звернути увагу на те, що результат виконання першого рядка коду (з використанням функції Str $) не залежить від національних установок, а другий – залежить.

Рада 20. Забудьте про цілочисельних значеннях булевих змінних

Програмний код

Dim i As Integer
i = True
If i = -1 Then MsgBox "Істина"
Else MsgBox "Брехня"
End If

видаватиме в Visual Basic 6.0 – “Істина”, а в Visual Basic.NET – “Брехня”. Причина в тому, що цілочисельне значення True помінялося с -1 на 1. Рекомендації очевидні: використовуйте тільки тип даних Boolean для логічних змінних і при роботі з ними застосовуйте тільки імена констант True і False. Іншими словами, наведений вище приклад слід записати так:

Dim i As Boolean
i = True
If i = True Then MsgBox "Істина"
Else MsgBox "Брехня"
End If

З одного боку, зміна числового значення для True виглядає досить логічно, оскільки булева алгебра завжди будувалася на логіці 0/1 (Брехня / Істина). До того ж це усуває важливе розходження з мовою C. Але, з іншого боку, це пов’язано з деякими внутрішніми проблемами
Visual Basic.

Поява свого часу значення -1 для True пояснюється тим, що фактично в якості булевої змінної використовується цілий тип даних і всі операції з ними на рівні машинних команд виконуються шляхом порозрядних операцій. Відповідно код

Dim a As Integer a = 0 "брехня a = Not a

приводив до результату -1.

Тут відзначимо одне протиріччя Basic: насправді цілочисельні змінні виступають у вигляді то чисел із знаковою розрядом (у арифметичних операціях і при використанні десяткових літералів), то без нього (в логічних операціях і в шістнадцятиричних і вісімкових літералах).

Але проблемою Visual Basic була (і залишається проблемою Visual Basic.NET) можливість неявного перетворення цілих чисел в булеві значення і навпаки. В результаті виконання наступного коду:

Dim intVar As Integer
Dim blnVar As Boolean
...
blnVar = intVar

при будь-якому ненульове значення intVar результатом завжди буде True. А це призводить до досить безглуздій ситуації, коли в результаті послідовності присвоєнь змінюється початкове значення:

Dim a As Integer
Dim b As Integer
Dim c As Boolean
a = 5
c = a
b = c
Print a; b

У Visual Basic 6.0 буде надруковано “5 -1”, в Visual Basic.NET – “5
1".

Зауважимо ще, що результат виконання наступного коду зовсім неочевидний:

Dim a As Integer
Dim c As Boolean
a = 5
c = 0
MsgBox c Or a

Хто з читачів зможе, дивлячись на цей код, впевнено передбачити результат операції?

Рада 21. Потрібно готуватися до поділу логічних операцій

Описані вище проблеми спільної обробки логічних і цілих змінних пов’язані ще і з тим, що, наприклад, ключове слово And виступає в залежності від контексту в ролі або логічного, або побітового And. В Visual Basic.NET ця невизначеність усунена – And відповідає тільки логічної операції зі змінними типу Boolean, а для побітових перетворень цілих чисел будуть використовуватися нові оператори – BitAnd, BitOr, BitNot, BitXor. Як наслідок, наведений вище код (останній фрагмент в попередньому розділі) в Visual Basic 6.0 видаватиме 5, а в Visual Basic.NET – True (1).

Покажемо те ж саме на прикладі наступного коду:

Dim a As Integer
Dim b As Integer
Dim c As Boolean
a = 1
b = 2
c = a And b MsgBox "Результат =" & c

У Visual Basic 6.0 буде отримана відповідь False (виконувалося порозрядне логічне множення двох цілочисельних змінних з подальшим перетворенням в логічне значення), а в Visual Basic.NET – True (Цілі числа спочатку були перетворені в логічні, а потім з ними виконали операцію And).

Для забезпечення сумісності коду Visual Basic.NET включає функції VB6.And, VB6.Or і VB6.Not, які еквівалентні існуючим сьогодні And / Or / Not (чомусь для Xor такої функції немає). Відповідно засоби поновлення коду перетворять наведений вище фрагмент наступним чином:

Dim a As Integer
Dim b As Integer
Dim c As Boolean
a = 1
b = 2
c = VB.And(a, b) MsgBox "Результат =" & c

Але тут потрібно відзначити, що наведений приклад використовує неявне перетворення типів, а тому в принципі потенційно небезпечний. Набагато краще відразу писати програми в такому вигляді:

Dim a As Integer
Dim b As Integer
Dim c As Boolean
a = 1
b = 2
c = (a <> 0) And (b <> 0) MsgBox "Результат =" & c

У цьому випадку ніяких проблем з переносом коду не буде і засоби автоматичного поновлення просто не знадобляться.

Рада 22. Приділяйте ще більше уваги логічним операціям

В Visual Basic.NET буде підвищена “інтелектуальність” логічних операцій для збільшення їх швидкодії. Це означає, що обчислення:

Dim a As Boolean
Dim b As Boolean
Dim c As Boolean
a = b And c

буде завершено достроково (з результатом False), якщо виявиться, що b = False. Це дуже добре, але тут є «підводний камінь», який видно з такого прикладу:

Dim b As Boolean
b = Function1() And Function2()

Справа в тому, що якщо значенням функції Function1 виявиться False, то звернення до Function2 просто не буде (а це може знадобитися). Засоби міграції виконають заміну коду:

Dim b As Boolean
b = VB6.And (Function1(), Function2())

але надійніше відразу записати програму в такому вигляді:

Dim b As Boolean
Dim c As Boolean
Dim d As Boolean
c = Function1()
d = Function2()
b = c And d

Рада 23. Використовуйте внутрішні константи

Хороший стиль програмування має на увазі, зокрема, використання внутрішніх глобальних констант Visual Basic замість літералів. Мова, звичайно ж, йде про роботу з елементами управління, методами і процедурами, які використовують в якості параметрів фіксовані набори значень:

Me.WindowState = vbNormal
MsgBox "Error!", vbCritical
txtTextbox.Visible = True

Наведений варіант набагато краще такого коду, що використовує числові літерали або змінні:

x = 16
Me.WindowState = 0
MsgBox "Error!", x
txtTextbox.Visible = -1

Використання імен констант суттєво підвищує наочність коду (Ім’я говорить про суть виконуваної операції) і знижує ймовірність помилок через тривіальних помилок. Втім, змінні, звичайно, часом навіть потрібно використовувати, але формувати їх краще за допомогою знову-таки констант:

x = vbCritical
...
MsgBox "Error!", x

Ще одна важлива перевага – мінімізація можливих ускладнень при оновленні програм і перенесення коду, наприклад, при зміні версій. В випадку з переходом до Visual Basic.NET подібні проблеми як раз можуть виникнути, тому що в ньому змінені значення (і навіть в деяких випадках імена), зокрема для тієї ж True.

Рекомендація використовувати константи відноситься не тільки до вбудованих функцій Visual Basic, а й до створюваного вами коду. Наприклад, якщо у вас є такий код, який збільшує кілька змінних на однакову величину:

a = a + 2
b = b + 2

то навіть для двох змінних має сенс написати код наступним чином:

Const myStep = 2
a = a + myStep
b = b + myStep

Це підвищує надійність і спрощує оновлення коду. Адже змінних може бути набагато більше, а наведені рядки коду можуть бути в реальності розкидані по програмі (можна легко не помітити, де потрібно виконувати оновлення).

Рада 24. Для роботи з датами використовуйте тільки тип Date

У всіх існуючих до сих пір версіях Visual Basic для зберігання дати фактично використовувалися змінні типу Double (окремий тип Date з’явився тільки у версії 4.0). Дата записана в числовому форматі IEEE, при цьому ціла частина відповідає номеру діб, починаючи від 30 грудня 1899 року, а дробова – часу доби. Можливо, цей формат збережений і в Visual Basic.NET, але вільність в перетворенні дат з обробкою їх як звичайних чисел буде припинена. Подібні безглузді конструкції, цілком допустимі в Visual Basic 6.0:

Dim a As Date
Dim d As Double
a = 1101.27 MsgBox a "видається 05.01.1903 6:28:48
d = Now * 1.4 + 0.347
a = d MsgBox a "видається 29.09.2041 4:48:35

в Visual Basic.NET працювати не будуть. Для обробки дат потрібно використовувати тільки набір відповідних функцій, яких більше ніж достатньо.

Рада 25. Не використовуйте недокументовані функції

Багато VB-програмісти навіть не підозрюють про існування недокументованих функцій VarPrt, VarPrtArray, VarPrtStringArray, ObjPrt і StrPrt, що дозволяють отримувати значення адреси пам’яті, де зберігаються відповідні змінні. Але вони часом дуже корисні при роботі з Win API, зокрема коли потрібно виконувати витончені операції з буферами.

Microsoft заявляє, що в Visual Basic.NET таких функцій не буде. Втім, корпорація замовчувала про їх існування протягом десяти років. Може бути, подібні функції залишаться, але тільки під іншими, секретними іменами?

Рада 26. Не використовуйте LSet для структур

У Visual Basic 6.0 оператор LSet можна було використовувати для присвоєння змінної одного користувальницького типу значення змінної іншого користувача типу. Тепер така можливість виключена.

Рада 27. Краще не використовувати рядки фіксованої довжини

В Visual Basic.NET рядки фіксованої довжини не будуть “рідними” (не будуть входити до складу базового набору типів даних, які безпосередньо підтримуваних транслятором). Для забезпечення сумісності буде використовуватися спеціальний об’єкт, тому код Visual Basic 6.0:

Dim MyFixedLengthString As String * 12

буде перетворений в

Dim MyFixedLengthString As New VB6.FixedLengthString(12)

Але слід мати на увазі, що застосування такого об’єкта буде обмежена. Наприклад, його можна буде використовувати при роботі з функціями Win API. Таким чином, наприклад, замість варіанта резервування буфера:

Dim Buffer As String * 25

потрібно писати:

Dim Buffer As String
Buffer = String$(25, " ")

Рада 28. Будьте уважні при використанні рядків і масивів в структурах

Ще одна очікувана проблема полягає в тому, що при визначенні структур (які також називаються призначеними для користувача типами даних) не буде автоматично виконуватися створення класу рядки фіксованої довжини і масиву фіксованого розміру. При оновленні такої структури в Visual Basic.NET вона буде позначатися коментарем з відповідним попередженням. Щоб уникнути проблем, краще відразу замінити наступний код:

Private Type UserType
UserArray(10) As Integer
UserFixedString As String * 30
End Type
Sub UserProc()
Dim UserVariable As UserType
End Sub

на такий:

Private Type UserType
UserArray() As Integer
UserFixedString As String
End Type
Sub UserProc()
Dim UserVariable As UserType
ReDim UserVariable.UserArray(10) As Integer
UserVariable.UserFixedString = String$(30, " ")
End Sub

Парадокс даної зміни у формуванні структур полягає в тому, що в перших реалізаціях користувацьких типів (це було в QuickBasic в середині 80-х) як полів допускалося використання тільки змінних фіксованої довжини. Можливість застосування масивів і рядків змінної довжини з’явилася набагато пізніше.

Рада 29. Не використовуйте As Any при зверненні до Win API

Безліч функцій Windows API мають можливість приймати параметри різних типів – інтерпретація переданих даних виконується в Залежно від значення інших параметрів. Приклад такої функції – SendMessage, в якій в якості останнього параметра може виступати як ціле число, так і рядок байтів довільної довжини. Для таких випадків Visual Basic використовує опис параметрів As Any, яке говорить про те, що в стек буде поміщений певний адресу буфера пам’яті. Фактично це означає, що компілятор знімає з себе відповідальність за контроль переданих даних.

Практично у всіх рекомендаціях по роботі з Win API йдеться про те, що необхідно особливу увагу при використанні опису As Any, і дається порада використовувати декілька псевдонімів (Alias) зі створенням двох і більше оголошень для однієї і тієї ж функції, причому в кожному з описів вказуються параметри певного типу. В Visual Basic.NET це буде вже не побажання, але вимога – в ньому виключена можливість використання типу As Any.

Ми розглянемо можливість застосування As Any, що підстерігають тут програміста небезпеки і варіанти використання Alias ​​на прикладі функції
lread:

Declare Function lread Lib _
"kernel32" Alias "_lread" _
(ByVal hFile As Long, lpBuffer As Any, _
ByVal wBytes As Long) As Long

У Visual Basic аналог цього – оператор Get при читанні файлів типу Binary. Звернемо відразу увагу на необхідність використання ключового слова Alias ​​в оголошенні функції. Справжні назви функції в бібліотеці починаються з символу «підкреслення» (стиль, типовий для мови C), що не дозволяється в Visual Basic.

В даному випадку параметр lpBuffer – це саме адресу буфера, куди будуть зчитуватися дані (wBytes вказує число читаних байтів). В як буфер може виступати проста змінна, масив, рядок.

"Читання дійсного числа, 4 байта
Dim MyVar As Single
wBytes = lread (hFile&, MyVar, Len(MyVar)
... "Читання 10 елементів масиву
Dim MyArray(0 To 9) As Byte
wBytes = lread (hFile&, MyArray(0), Len(MyArray(0))* 10)

Зверніть увагу: другий аргумент функції передається по посиланню, решта – за значенням.

Однак, якщо ви хочете прочитати символьні дані у рядок змінної довжини, вам потрібно використовувати звернення іншого виду:

"Читання символьної рядки, 10 символів
Dim MyVar As String MyVar = Space$(10)
wBytes = lread (hFile&, ByVal MyVar, Len(MyVar))

Тут видно важлива відмінність від наведеного раніше прикладу – строкова змінна обов’язково супроводжується ключовим словом ByVal. Якщо ми цього не зробимо, то буде передаватися не адресу рядка, а адреса її дескриптора, і фатальна помилка буде неминуча.

Щоб убезпечити себе, можна додати спеціальне опис цієї ж функції для роботи тільки з рядковими змінними:

Declare Function lreadString _
Lib "kernel32" Alias "_lread" _
(ByVal hFile As Long, ByVal lpBuffer As String, _
ByVal wBytes As Long) As Long

При роботі з цим описом вказувати ByVal при зверненні вже не потрібно:

wBytes = lreadString (hFile&, MyVarString, Len(MyVarString))

Правда, повна відмова від використання As Any має і свої мінуси, в Зокрема, як раз при роботі з функцією lread – доведеться дублювати її описи для всіх можливих варіантів простих змінних.

Рада 30. Точно оголошуйте спосіб передачі параметрів

Зазвичай ми вказуємо спосіб передачі параметрів – ByRef (за посиланням) або byVal (за значенням) – тільки при роботі з Win API (точніше, з усіма DLL-функціями). У попередньому раді ми показали, як це важливо. При роботі з процедурами всередині середовища Visual Basic історично було прийнято, що за замовчуванням параметри завжди передаються по посиланню. Але в Visual Basic.NET це змінилося – тепер за умовчанням всі параметри будуть передаватися за значенням.

Безумовно, це позитивна зміна, оскільки воно забезпечує більш надійне програмування – спеціальним чином повинна описуватися саме можливість повернення зміненого параметра. Тим більше, що двосторонній обмін даними зустрічається на практиці набагато рідше, ніж односторонній. Але при переході від Visual Basic 6.0 до Visual Basic.NET це зміна може викликати неприємності:

Dim a%, b%, c%
a = 0: b = 1: c = 2
Call MyTest(a, b, c)
MsgBox a & " " & b & " " & c
Public Sub MyTest(ByVal d%, f%, ByRef g%)
d = 10: f = 11: g = 12
End Sub

При роботі в Visual Basic 6.0 буде отриманий результат

0 11 12

а в Visual Basic.NET:

0 0 12

Сподіваюся, що кошти міграції забезпечать заміну опису підпрограми на

Public Sub MyTest(ByVal d%, ByVal f%, ByRef g%)

але краще визначати в явному вигляді повертаються параметри вже зараз.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*