第4章 スムーズな移行のためのポイント~プログラミング編~

プログラミングに関する変更のポイントを知っておくと、Visual Basic .NETを使う際にスムーズな移行ができます。また、今Visual Basic 6.0でどのように書いておけばよいかの指針が見つかります。
ここでは、Visual Basic 6.0のユーザーを対象に、「Visual Basic .NETへの移行」にフォーカスしたVisual Basic .NETの特徴を紹介していきます。

■ 変更ポイント
Visual Basic 6.0から Visual Basic .NETでどう変わるか、またVisual Basic 6.0からVisual Basic .NETで、文法がどのように変更されるかを紹介します。

アップグレードウィザードによる変更点
アップグレードウィザードがどのような変更を加えてくれるかを紹介します。

今、何をしておくべきか?
Visual Basic .NETでの変更に備えて、現在開発中のVisual Basic 6.0のプロジェクトをどのように構成しておけばよいかの指針を紹介します。

関連・補足項目
各項目に関連する開発上のヒントや補足事項を紹介します。

  • 変更点だけを効率よく知りたい方は 変更ポイントのみ、または 変更ポイント関連・補足項目を読みください。
  • アップグレードウィザードの効果を知りたい方はアップグレードウィザードによる変更点をお読みください。
  • Visual Basic .NETへの移行を考慮したVisual Basic 6.0のプログラミングのポイントを知りたい方は 変更ポイント今、何をしておくべきか?をお読みください。
  • Visual Basic .NETを予習し、スムーズな移行を目指す方は変更ポイントから関連・補足項目まで、すべてお読みください。

1. 変数宣言の変更

  • Option Explicit Onがデフォルト
  • Dim x, y, z As String
  • ブロック内の変数のスコープ

■ 変更ポイント

Option Explicit(変数宣言の強制)

変数宣言を強制するために、Option Explicit Onを利用します。また、Visual Basic .NETでは「Option Explicit On」がデフォルトになりました。つまり変数宣言を強制されたくない場合には、Option Explicit Offと明示的にソースコードに記述する必要があります。

変数宣言時の型指定

Dim x, y, z As String

この宣言は、Visual Basic 6.0では、2つのVariant型データ(xとy)と、ひとつのString型データ(z)の生成を意味します。
Visual Basic .NETでは、この宣言で3つのString型データが生成されます。

変数のスコープ

Visual Basic 6.0では、ローカル変数の宣言が含まれている行からプロシージャの末尾まで、ローカル変数を参照することができます。
Visual Basic .NETでは、ブロック内で宣言されている変数は、そのブロック内のみ利用可能です(リスト1)。

Option Explicit On
(略)
Sub Proc1()
    Dim x As Integer
    If x = 100 Then
        Dim a As String ─────┐aのスコープ
        a = “Hello” ──────┘
    Else
        a = “Hi” ‘ VB .NETではエラー
    End If
    a = “Hi” ‘ VB .NETではエラー
End Sub
リスト1:VB .NETの変数スコープ

変数宣言が強制されている場合、スコープ外での変数の利用はコンパイルエラーになります。
Ifステートメントの場合はIfの中で宣言したデータを利用できるのはIfからElseまでになります。
ただし、同じ名前の変数の宣言は、プロシージャの中で1度だけしかできません。したがってリスト2のコードはコンパイルエラーになります。

Option Explicit On
(略)
Sub Proc1()
    Dim a As String
    Dim x As Integer
    If x = 100 Then
        Dim a As String
        a = "Hello"
    End If
End Sub
リスト2:変数名はプロシージャの中で1度だけ

■ アップグレードウィザードによる変更点

[Visual Basic 6.0 のコード][Visual Basic .NET にアップグレード]
Dim x, y, z As String
Dim x, y As Object
Dim z As String

また、Visual Basic 6.0で以下のようにブロック内で宣言していたデータは、リスト3のように、アップグレードウィザードがブロックの外で宣言するように変更してくれます。

リスト3:スコープの変更を考慮したコードの変更(アップグレードウィザード)

■ 今、何をしておくべきか?

Visual Basic 6.0でも変数は適切なデータ型を指定して宣言することが推奨されていました。
Visual Basic .NETでは、Option Explicit Onがデフォルトになります。つまり、Visual Basic 6.0でも適切なデータ型を指定しておくことで、Visual Basic .NETでの変更を最小限に抑えることができます。

2. すべてがオブジェクト型

  • 基本データ型(Integer、String)→オブジェクト型
  • Variant→Object

■ 変更ポイント

Visual Basic .NETでは、IntegerやStringなど、基本データを含むすべての変数がオブジェクトになりました。また、汎用データ型は、Variantではなく、Objectを使用し、型を指定しない変数のデータ型もObjectとなります。
たとえば以下のように記述することにより、安全性や、他の.NET言語との互換性が高まるだけでなく、変数の持つ便利なメソッドやプロパティを利用することができます。

Dim str1 As String, x As Integer
str1 = “ABCDEFG”
x = str1.Length ‘ 文字列の長さを取得(StringオブジェクトのLengthプロパティ)

最初はこの変化に戸惑うこともあるかもしれませんが、使ってみるととても便利で、直感的なプログラムができる(しかも、安全に)ということに気づくでしょう。

■ アップグレードウィザードによる変更点

基本データ型は、対応するデータ(オブジェクト)に変更されます。といってもデータ型名に変更のないものも多いので、見た目には変化がない場合がほとんどです。

[Visual Basic 6.0のコード][Visual Basic .NETにアップグレード]
Dim str1 As String
Dim birthday As Date
Dim str1 As String
Dim birthday As Date

しかし、実際のデータ型は変更しています。
また、Visual Basic .NETでは通貨型(Currency)はなくなりますが、代わりに大きな数値を扱うことができるDecimalを利用します。そのため、Visual Basic 6.0の、

Dim cur As Currency

は、

Dim cur As Decimal

に変更されます。
もちろん、Variantや型指定なしの宣言はObjectになります。

[Visual Basic 6.0のコード][Visual Basic .NETにアップグレード]
Dim x As Variant
Dim y
Dim x As Object
Dim y As Object

整数型については、本章の「Short、Integer、Long」を参照してください。

■ 今、何をしておくべきか?

必要に応じてアップグレードウィザードが変更を行なってくれるので、きちんと変数宣言をして整数を利用していれば問題はありません。変数を利用するとき(悪い意味で)テクニカルな使い方をしていると、Visual Basic .NETで変更が必要になります。たとえば、Booleanのデータを数値として利用したり、Visual Basic 6.0のDate型の内部構造がDouble型である(PlusOne参照)ことを利用(悪用?)したり、といった使い方です。

■ 関連・補足項目

共通型システム(CTS:Common Type System)

.NET Frameworkでは、すべての言語間で共通の型システムを使用します。ここで扱うデータは、基本データ型も含め、すべてオブジェクトです。共通型システムを利用することで、言語間の型の問題が解決され、相互に利用しやすくなります。

値型と参照型

「すべてオブジェクト」といいましたが、実はデータは値型(オブジェクト自身)か参照型(オブジェクトへの参照)のいずれかに分類できます。値型に分類されるのは、基本データ型(ただし、StringとObjectを除く)と列挙型と構造体(ユーザー定義型)です。それ以外は参照型です。

Dim x, y As Integer
x = 10
y = x

上のコードでは、yに10という値が代入されています。この後、xやyを変更しても互いに影響は与えません。
それは、xとyが値型だからです。
一方、次のプログラムでは、objStudentXとobjStudentYが①で作成した同一のオブジェクトを参照します。

Dim objStudentX, objStudentY As clsStudent
objStudentX = New clsStudent() ─── ①
objStudentY = objStudentX

そのため、一方の変更が他の参照に影響を与えます。
つまり、次のコードでは、「Akiko」が表示されます。

objStudentX.FName = "Akiko"
MsgBox(objStudentY.FName)

これが参照型の特徴です。
また、値型と参照型は初期化にも違いがあります。値型の場合は、変数宣言時にオブジェクトが作成されます。一方、参照型では変数が宣言されただけでは無効な状態(Nullが設定されている状態)になっています。Newキーワードでオブジェクトを生成し、それを代入することではじめて参照がセットされます。

● Plus One

Date型

Visual Basic 6.0ではDate型の内部はDouble型でした。そのため、日付の格納と操作にDoubleデータ型を使用することができました。
Visual Basic .NET では、日付は内部的にDoubleとしては格納されないため、そのような使い方をしていると修正が必要になります。
たとえば、次のコードはVisual Basic 6.0では有効ですが、Visual Basic .NETでそのまま利用するとコンパイルエラーになります。

Dim dbl As Double
Dim dat As Date
dat = Now
dbl = dat ‘ Double型の変数に Date型の値を代入
dbl = DateAdd(“d”, 1, dbl) ‘ Double型データを日付関数で使用
dat = CDate(dbl) ‘ DateによるDouble型データのDate型への変換

.NET Frameworkには、DoubleとDateの間で変換を行なうための ToOADateおよびFromOADate関数があるので、これを利用して型変換を行なうことも可能です。ちなみに、上記のコードはアップグレードウィザードによって次のように変換されます。

Dim dbl As Double
Dim dat As Date
dat = Now
dbl = dat.ToOADate
dbl = DateAdd(Microsoft.VisualBasic.DateInterval.Day, 1, System.Date.FromOADate(dbl)).ToOADate
dat = System.Date.FromOADate(dbl)

今回の場合は、幸運にも適切にアップグレードされましたが、常にプログラムの意図が正しく解釈されるとは限りません。
Visual Basic .NETでの不必要な変更を避けるために、日付の格納には必ず Date型を使用するようにして、操作の際にDouble型として扱うのは避けてください。

3. Short、Integer、Long

  • Shortは16ビット
  • Integerは32ビット
  • Longは64ビット

■ 変更ポイント

整数データは表1のように変わりました。

Visual Basic 6.0Visual Basic .NETC#マネージドC++
16ビットIntegerShortshortshort
32ビットLongIntegerintint
64ビット(none)Longlong_int64
表1:.NETで変更された整数データ

■ アップグレードウィザードによる変更点

アップグレードウィザードによって、

[Visual Basic 6.0のコード][Visual Basic .NETのコード]
Dim x As Integer ‘ 16ビット
Dim y As Long ‘ 32ビット
Dim x As Short ‘ 16ビット
Dim y As Integer ‘ 32ビット

というように、大きさが同じ対応するデータ型に変更されます。

■ 今、何をしておくべきか?

変数の大きさの変更にともない、32ビットCPUでの最適化を図るためにLongを使っていた場合には、Integerへの変換が必要です。
もちろん、アップグレードウィザードを使えば、適切にアップグレードしてくれるので、問題はありません。

● Plus One

この変更により、SQL Serverのデータ型と同じ大きさになったため、バグが少なくなるという(うれしい)副作用もあります。

4. デフォルトプロパティ

  • デフォルトプロパティは使えない

■ 変更ポイント

Visual Basic 6.0 では、デフォルトプロパティを使うことができました。
たとえば、

Text2.Text = Text1.Text

というコードは、デフォルトプロパティを省略して、

Text2 = Text1

と、コーディングすることができました。Visual Basic .NETでは「デフォルトプロパテ ィ」は使わなくなりました(PlusOne参照)。なお、例外については、 関連・補足項目を参照してください。

■ アップグレードウィザードによる変更点

アップグレードウィザードは、デフォルトプロパティを自動解決してくれます。
Visual Basic 6.0の、

Text2 = Text1 ' OKなパターン

というコードは、自動的に、

Text2.Text = Text1.Text

にしてくれます。 また、Visual Basic 6.0の、

Dim obj As TextBox ' これもOKなパターン
Set obj = Form1.Text1
MsgBox obj

というコードも、アップグレードウィザードが最後の行を、

MsgBox obj.Text

としてくれるので、変換後に何もしなくても動作します。
ただし、この「自動解決」をしてくれない場合もあります。それは、Visual Basic 6.0で書かれた以下のようなコードです。

Dim obj As Object ' これは×なパターン
Set obj = Form1.Text1
MsgBox obj

変数をObject型として宣言し、実行時にクラスのインスタンスを代入するというこのパターンは、いわゆる実行時バインディングになっていて、オブジェクトが特定できません。そのためデフォルトプロパティの自動解決も行なえず、このままではエラーになってしまいます。そこで変換後、コードの手動変更が必要となります。

■ 今、何をしておくべきか?

オブジェクト変数の実行時バインディングは避けましょう。
また、アップグレードウィザードを通さず、コピー&ペーストでVisual Basic 6.0の コードを再利用しようと思うのであれば、デフォルトプロパティの利用も避けておくと無難です。

■ 関連・補足項目

デフォルトプロパティ使用不可の例外

コレクションのようなパラメータ付きのデフォルトの使用は可能です。たとえば、

rs(“CompanyName”).Value ‘ OK 

は、Visual Basic 6.0でも、Visual Basic .NETでも、

rs.Fields(“CompanyName”).Value 

のFieldsコレクションを省略したものとして解釈されます。  

● Plus One

なぜ、デフォルトプロパティがなくなったのでしょう?

たとえば、Visual Basic 6.0では、オブジェクトの代入にSetステートメントを利用しました。オブジェクトXとYがあったとき、

Set Y = X

は、X(オブジェクト)をYに代入することを意味します。そして、Xのプロパティを代入したいときは、

 Y.プロパティ = X.プロパティ 

と書くか、あるいは、このプロパティがデフォルトである場合は省略でき、

Y = X

と記述することができます。 Visual Basic .NETでは、オブジェクトの代入にも変数の代入にもSetステートメント を使う必要がなくなりました。

Y = X

と記述した際、(参照型であれば)Xが参照しているオブジェクトへの参照がYにセットされます。一方、「Xのプロパティ」を「Yのプロパティ」代入したいときには、

 Y.プロパティ = X.プロパティ

と明示します。これにより、プログラムが明確になり、あいまいさがなくなるという利点があります。

5. プロパティの変更

  • CaptionプロパティはTextプロパティに

■ 変更ポイント

いくつかのプロパティが変更されています。
代表例は、フォームやラベルコントロールの文字列を設定するCaptionプロパティが、 Textプロパティになったことです。ほかにも、リストボックスやコンボボックスコント ロールのListIndexプロパティがSelectedIndexプロパティに変更されています。
これらは、他の.NET言語とプロパティ名をそろえるためです。

■ アップグレードウィザードによる変更点

アップグレードウィザードによって、これらのプロパティを利用しているコードは すべて自動的に変更されます。

[Visual Basic 6.0のコード][Visual Basic .NETにアップグレード]
Label1.Caption = “Hello”Label1.Text = “Hello”

ですから、ほとんどの場合、問題はありません。
ただし、実行時バインディングを利用していると、正しく変更されません。たとえばVisual Basic 6.0で、

Dim obj As Object
Set obj = Form1.Label1
obj.Caption = “Hello”

というコードを記述していた場合、「obj」の型が特定できないため、変更してくれません。そのため、手動でCatpionプロパティをTextプロパティに変更しなくてはなりません。

■ 今、何をしておくべきか?

Visual Basic .NETで手動変更をなくすためには、上のような実行時バインディングではなく事前バインディングを使用します。つまり、あらかじめ、

Dim obj As Label
Set obj = Form1.Label1
obj.Caption = “Hello” 

のようにVisual Basic 6.0でコーディングしておくことをお勧めします。
このようにしておけば、「obj」の型がラベルであることがわかるので、アップグレードウィザードがCaptionプロパティをTextプロパティに変更してくれます。
オブジェクトを扱う変数は、Object型やVariant型として宣言するのではなく、可能な 限り具体的なオブジェクトとして宣言するようにしてください。

● Plus One

よいプログラムを作成するために

Visual Basic .NETでは、「適切なデータ型を宣言し、正しくデータを利用するプログラミング」が求められます。なぜかというと、厳密な型チェックを行なうことでエラーの入り込みにくいプログラミングを実現するためです。
このことは、Visual Basic 6.0でも推奨されていたことでした。ですから、Visual Basic 6.0でも、きちんとデータ型を指定してプログラミングをすることで、よりエラーの入り込みにくいプログラムを作ることができ、同時にVisual Basic .NETでの変更 を軽減できます。
また、どうしても、実行時バインディングを利用したい場合には、明示的な変換関 数を使用することをお勧めします。これにより、コードの意図がわかりやすくなり、プロジェクトの Visual Basic .NETへの移行をスムーズにします。
Visual Basic .NETには、データ型の宣言を強制するための「Option Strict On」というオ プションがあります。これは、「Option Explicit」と同様、コードの先頭に記述します。
これにより、次の事柄ができなくなり、実行の安全性が高まります。

・明示的なキャスト(型変換)なしで、データを異なる型に代入する
・実行時バインディング
・Asなしの宣言
・Object型オブジェクトの使用制限(=、<>、Is、TypeOf…Is以外での使用不可)

コードで「Option Strict」を指定しない場合、プロジェクトのプロパティ([共通プロパティ]-[ビルドプロパティ])で設定可能です。この設定のデフォルトは「Off」 になっています。適切なコーディングの強制のために頁単位で「Option Strict On」の 記述、またはプロジェクト単位での設定をお勧めします。

6. 配列のインデックス

  • 配列のインデックスは0から始まる

■ 変更ポイント

Visual Basic 6.0では、任意の上限、下限を持つ配列を宣言することができました。
また、Option Baseステートメントで下限を指定しなかった時のデフォルトを「0(デ フォルト)」または「1」に指定することもできました。
Visual Basic .NETでは、他の.NET言語との相互利用性を重視し、インデックスの下限は必ず「0」、つまり0オリジンの配列になります。上限については、従来と変更はありません(PlusOne参照)。
また、「インデックスは必ず0から」になるため、Option Baseステートメントはなくなりました。

Dim array(5) As Integer

は、Visual Basic 6.0でもVisual Basic .NETでも、インデックスが0~5までの要素数6の 配列を宣言したことになります。
ちなみに、Visual Basic 6.0で記述可能だった、

Dim array(10 to 12) As Integer  

という宣言は、Visual Basic .NETではできません。

■ アップグレードウィザードによる変更点

アップグレードウィザードにより「Option Base ステートメント」は削除されます。
また、インデックスの下限が0以外の配列は0オリジンの配列に変更され、アップグレードコメントが付加されます。

[Visual Basic 6.0のコード][Visual Basic .NETにアップグレード]
‘ 要素数 3
Dim array(10 To 12) As Integer
‘ 要素数 13
Dim array(12) As Short

このときの要素数に注目してください。確かにこの配列を利用していたプログラムは引き続き配列を利用できますが、このままでは使われていない無駄な領域が存在することになります!!
そこで、効率よくメモリを利用するために、手動でコードを変更しなくてはなりません。

■ 今、何をしておくべきか?

このような手動による変更をしなくてすむようにするためには、Visual Basic 6.0の プログラムでも、できるだけ0オリジンの配列を利用しておくとよいでしょう。

● Plus One

Visual Basic 6.0からの変更点として、Visual Basic .NETでは、他の言語との相互運用性 を重視しインデックスの下限は「0」となりましたが、上限はVC++ .NETやC# .NETと は異なります。
各言語での配列宣言の例は次の通りです。

[C# .NETの場合]
int [] ar = new int[5]; // 要素数 5(インデックスは0~4)

[VC++ .NETの場合]
System::Int32 ar[] = _gc new System::Int32[5]; // 要素数 5(インデックスは0~4)

[Visual Basic .NETの場合]
Dim ar(5) As Integer ' 要素数 6(インデックスは0~5)

7. 固定長文字列

  • 固定長文字列はサポートされない

■ 変更ポイント

固定長文字列は、(基本データ型としては)サポートされなくなりました。それは、 配列と同様に他の .NET言語との互換性を持たせるためです。

■ アップグレードウィザードによる変更点

固定長文字列は、Visual Basic固有の「VB互換文字列(固定長)」に変換されます。

[Visual Basic 6.0のコード][Visual Basic .NETにアップグレード]
Dim str1 As String * 10 Dim a As VB6.FixedLengthString = New VB6.FixedLengthString(10)

このようにVisual Basic .NETには、固定長文字列の動作を提供する互換性クラスが 用意されているため利用できます。そのため、アクセスが低速であることと、.NETの 他の言語との互換性の問題を除けば、大きな問題はないでしょう。

■ 今、何をしておくべきか?

もちろん、今から固定長文字列をできるだけ利用しないようにしておけば、Visual Basic .NETでも速度や互換性の問題は発生しません。

■ 関連・補足項目

固定長文字列をユーザー定義型の中で利用する場合に発生する問題に関しては、「ユ ーザー定義型(構造体)の中の配列と固定長文字列」を参照してください。

8. 構造体の中の配列と固定長文字列

  • 構造体の宣言時
    • 配列の要素数は指定できない
    • 固定長文字列は使えない

■ 変更ポイント

Visual Basic 6.0では、ユーザー定義型の要素として、配列を宣言するとき、その要素数を指定できました。また、固定長文字列も指定できました。 ユーザー定義型は、Visual Basic .NETでは構造体(Structure)といいます。この構造体の宣言時に、配列の要素数の指定や、固定長文字列を使うことはできなくなりました。

■ アップグレードウィザードによる変更点

アップグレードウィザードは、これらの変更ポイントを自動で変更してくれます。
次のようなVisual Basic 6.0で作ったユーザー定義型をアップグレードすると、

[Visual Basic 6.0のコード]

Private Type MyType
    MyArray(5) As Integer
    MyString As String * 100
End Type

次のような、構造体になります。

[Visual Basic .NETにアップグレード]

Private Structure MyType
    Dim MyArray() As Short
    <VBFixedString(100), System.Runtime.InteropServices.Marshal As _
      System.InteropServices.UnmanagedType.ByValTStr, SizeConst:=100)>
    Dim MyString As String
    Public Sub Initialize()
        ReDim MyArray(5)
    End Sub
End Structure

この構造体には、配列の要素数を初期化するためのユーザー定義関数(Initialize)が 含まれています。この関数を利用して配列の要素を初期化するよう、TODOコメントも 追加されるので、このコメントを参考に、配列の要素数を初期化します。
また、固定長文字列については、可変長文字列に変更されていますが、固定配列や マーシャリングのための属性が追加されているので、固定長文字列のように利用できます。

■ 今、何をしておくべきか?

この文法の変更に対処する方法はふたつあります。
たとえば、Visual Basic .NETでの変化に備えて、あらかじめ固定長文字列を利用しないようにすることです。たとえば、以下のように可変長文字列にしておき、必要に応じて配列の要素数を指定する初期化処理を行ないます。

Private Type MyType
    MyArray() As Integer
    MyFixedString As String
End Type

Sub Bar()
    Dim MyVariable As MyType
    ReDim MyVariable.MyArray(5) As Integer
    MyVariable.MyFixedString = String$(100, ” “)
End Sub

あるいは、初期化のための関数を作成し、それを利用して要素数を指定してもよいでしょう。
そして、もうひとつの対処法は、Visual Basic .NETになってからアップグレードウィザードでできる限り変換後、必要に応じて手動で修正するという方法です。
このいずれかの選択になります。

9. Dim x As New MyClass

  • Dim x As New MyClassの動作が変更

■ アップグレードウィザードによる変更点

Visual Basic .NETでは、このコードによる動作が変更されました。
Visual Basic 6.0では、このコードのある行ではオブジェクトは生成されずに、それを利用するタイミングに、オブジェクトがすでに作られているかどうかをチェックし、なければ作成されるというものでした。これによる利点と欠点は次の通りです。

利点(Visual Basic 6.0)

必ずオブジェクトが存在することが保証される(Nothingを設定してオブジェクトが破棄されたとしても、オブジェクトを再利用しようとすると、再作成される)

欠点(Visual Basic 6.0)

オーバーヘッド(オブジェクトが利用されるたびに毎回チェックが入る)
明示的なオブジェクトの管理がしにくい

Visual Basic .NETでは、

Dim x As New MyClass()

は、

Dim x As MyClass
x = New MyClass()

を1行で書き表わしたものにすぎません。そのため、Visual Basic 6.0とは異なり、Newされるのは後にも先にも1回だけです。もちろんオーバーヘッドもありません。

■ アップグレードウィザードによる変更点

アップグレードウィザードではコードは変更されません。ここでポイントとなるのは、コードは変更されないけれども、その意味は違ってしまっているという点です。
Visual Basic 6.0でこのコードを使っている場合、上記の利点(常にオブジェクトがあることが保障されている)を期待している場合が多いでしょう。しかし、Visual Basic .NETではその限りではないことに注意しなくてはいけないのです。もし、Visual Basic 6.0で「オブジェクトがあることを常に期待するプログラム」を書いているときは、Visual Basic .NETでは変更が必要になります。

■ 今、何をしておくべきか?

原則として、Visual Basic 6.0では、

Dim x As New MyClass

はオーバーヘッドが大きいので、使うべきではありません。どうしても、そのようなプログラムが便利だという場合は、Visual Basic .NETになったときに、コードを変更する覚悟が必要です。

● Plus One

Visual Basic .NETでも、常にオブジェクトが存在することを期待するプログラミングを実現したい場合、例外処理を利用することもできます。Visual Basic .NETでは、オブジェクトを利用しようとした際に、オブジェクトが見つからないと例外を検出するので、必要に応じて

x = New MyClass

を行なうというコードを書くこともできます。

10. VB定数

  • VB定数は、名前や値が変更されている

■ 変更ポイント

Visual Basic の組み込み定数(VB定数)は、その名前と値の多くが変更されています。
たとえば、

vbMaximized
↓
System.Windows.Forms.FormWindowState.Maximized(Enum)

vbHourglass
↓
System.Windows.Forms.Cursors.WaitCursor(Cursorオブジェクト)

などのように、vbXXXではなく他の言語と共通のオブジェクトに変更になっています。

■ アップグレードウィザードによる変更点

VB定数は適切に変更されます。

Form1.MousePointer = vbHourglass
Form1.MousePointer = 11

などは、いずれも、

Form1.DefInstance.Cursor = System.Windows.Forms.Cursors.WaitCursor

のように、適切に変更されます。
しかし、VB定数の代わりにVB定数に相当する値を使用している場合、アップグレードが行なわれないこともあります。たとえばVisual Basic 6.0で、

x = 11
Form1.MousePointer = x

と記述したコードは、

x = 11
Form1.DefInstance.Cursor = x

のように、値はそのままで、しかも、アップグレードコメントなども付加されません。
このままの状態で変更せずにおくと、xのデータ型次第ではビルドエラーか実行時エラーが発生します。

■ 今、何をしておくべきか?

Visual Basic 6.0でも推奨されているとおり、きちんとVB定数を使用していれば、Visual Basic .NETでの手動変更を最小限に抑えることができます。

11. プロシージャ

  • 引数のデフォルトはByVal
  • プロシージャのStaticキーワードはサポートされない
  • プロシージャの呼び出しには“()”を付ける

■ 変更ポイント

引数のデフォルトはByVal

Visual Basic 6.0では、特に指定をしない場合、引数はByRefとなりましたが、Visual Basic .NETではByValになります。Visual Studio .NETでは、混乱を防ぐために、型を指定しないと自動的にByValが追加されるようになっています。

Staticプロシージャ

Visual Basic 6.0でプロシージャにStaticキーワードを指定すると、プロシージャ内のローカル変数がStatic変数になり、変数をコール間で共有することができました。Visual Basic .NETではプロシージャに対してStaticキーワードを指定することはできなくなりました。Static変数にしたいときは、変数宣言時に個別に指定します。

プロシージャの呼び出し

プロシージャの呼び出しの時は引数を必ず“(”と“)”で囲みます。引数がない場合は、()は任意ですが、Visual Studio .NETのコードエディタは自動的に()を追加します。

■ アップグレードウィザードによる変更点

引数のタイプを指定しない場合、Visual Basic 6.0ではByRefを意味します。そのため、Visual Basic 6.0による、

Sub Proc1(ByVal x As Integer, ByRef y As Integer, z As Integer)
    z = x + y
End Sub

というコードの第3引数の「z」は、アップグレードウィザードによって、以下のよう
にByRefが明示的に追加されます。

Sub Proc1(ByVal x As Short, ByRef y As Short, ByRef z As Short)
    z = x + y
End Sub

これは、何も書いていないと、Visual Basic .NETではByValを意味してしまうためです。
また、これを呼び出すための、

Proc1 a, b, c

は、()が追加され次のように変換されます。

Proc1(a, b, c)

● Plus One

Visual Basic では、関数名を利用して戻り値を設定していました。

<関数名> = <戻したい値> (Visual Basic 6.0とVisual Basic .NET)

Visual Basic .NETもこの方法をサポートしていますが、それに加え、Returnキーワードを利用して、

Return <戻したい値> (Visual Basic .NET)

とコーディングすることができるようになりました。これにより、コードの意味が明確になります。
また、Returnキーワードを利用すると、実行コードを生成するときに最適化がより適切に行なわれるため、パフォーマンスの面でも優れているといえます。関数名をReturnに変えるだけでよいのですから、ぜひ置き換えを行なってください。

12. プロパティプロシージャ

  • プロパティ構文が変更
  • アクセスレベルは、SetとGetでそろえる
  • デフォルトプロパティはコード中で指定(パラメータ付きのみ)
  • Friendは微妙に変更

■ 変更ポイント

プロパティ構文が変更になりました。Visual Basic .NETでは、

Public Property MyProp1() As Object
Get
‘ プロパティを取得する処理
End Get
Set
‘ プロパティを設定する処理
End Set
End Property

と、プロシージャを一体化して書きます。もちろん、取得専用または設定専用のプロパティ(★参照)を作成することもできます。Visual Basic 6.0では、プロパティのデータ型によってSetまたはLetを使い分けていましたが、すべてSetになります。
このコードの書き方からも予想がつくと思いますが、SetとGetのアクセスレベルをそろえる必要があります。これは、意味的にも正しいことですので、異なるアクセスレベルに設定できないことは、問題ではないでしょう。
また、Visual Basic 6.0でのデフォルトプロパティの設定は、「ツールの属性」メニューからプロパティを「既定値」に設定することで行なっていました。そのため、コードを見ただけでは、デフォルトプロパティがどれであるかがわかりませんでした。
Visual Basic .NETでは、コードの中にDefaultキーワードを挿入することで設定できるようになりました。

Default Public Property MyProp22(ByVal index As Integer) As Object
    Get
        ‘ プロパティを取得する処理
    End Get
    Set
        ‘ プロパティを設定する処理
    End Set
End Property

ただし、Visual Basic .NETではパラメータのないプロパティをデフォルトプロパティに設定することはできないことを忘れないでください。

■ アップグレードウィザードによる変更点

できる限りアップグレードしてくれます。たとえば、

Private mvarMyProp1 As Variant
Private Public Property Let MyProp1(ByVal vData As Variant)
    ‘ プロパティを設定する処理
End Property
Public Private Property Get MyProp1() As Variant
    ‘ プロパティを取得する処理
End Property

というVisual Basic 6.0のコードは、

Private mvarMyProp1 As Object
Public Property MyProp1() As Object
    Get
        ‘ プロパティを取得する処理
    End Get
    Set(ByVal Value As Object)
    ‘ プロパティを設定する処理
    End Set
End Property

とアップグレードされます。
次のような変更をチェックしてみて下さい。

・SetとGetの一体化
・LetをSetに変更
・アクセス指定子が異なる場合、Publicに統一

デフォルトプロパティの指定は、手動で追加して下さい。

■ 今、何をしておくべきか?

この変更に備えて、特にしておくことはありません。

■ 関連・補足項目

取得専用のプロパティにはReadOnlyキーワード、設定専用のプロパティにはWriteOnlyキーワードを指定します。
Set…End Setを記述しないときにReadOnlyキーワードを忘れるとエラーになります。また、ReadOnlyキーワードがあるときに、Set…End Setがあってもエラーになります。
Get…End Getを記述しないときにWriteOnlyキーワードを忘れるとエラーになります。また、WriteOnlyキーワードがあるときに、Get…End Getがあってもエラーになります。

● Plus One

Friendの効果が微妙に変化しました。
Visual Basic 6.0では、同一プロジェクトから参照したいが、外部に公開したくないプロパティなどにFriendと付けました。Visual Basic .NETでは、同一アセンブリ内から参照したいが、外部に公開したくないプロパティなどにFriendと付けます。
この2つは、ほとんど同じように見えます。たしかにVisual Studio .NETを利用している場合は同じといってよいかもしれません。しかし、厳密には、Friendはアセンブリ内であれば参照できますので、たとえば、Visual Basic .NETのFriendプロパティを同一アセンブリのC# .NETから利用できる、ということになるのです。これが「微妙」な変化です。

13. COMコンポーネントの利用

  • プロジェクトをアップグレードすると自動的に利用可能に

■ 変更ポイント

Visual Basic .NETからCOMコンポーネントを利用することができます。COMコンポーネントを利用するためには「参照設定」を行ない、オブジェクトを生成して利用します(作業ベースではVisual Basic 6.0とほとんど変わりはありません )。
Visual Basic .NETでは、COM以外の一般のオブジェクトを作成する場合、Newキーワードを利用し、CreateObject関数は使用しません。そして、COMのオブジェクトを生成する場合は、NewキーワードまたはCreateObject関数を利用します。

■ アップグレードウィザードによる変更点

COMコンポーネントを利用しているVisual Basic 6.0のプロジェクトをアップグレードすると、COMコンポーネントを利用するための参照がプロジェクトに自動的に追加されます。そのため、ほとんどの場合、手動でコードを変更することなくCOMコンポーネントを利用できます。
たとえば、次のようなCOMコンポーネントを利用するVisual Basic 6.0のコードは、

Dim x As MyProject.MyClass
Set x = New MyProject.MyClass
x.MyMethod1

アップグレードウィザードによって、次のように変更されます。

Dim x As MyProject.MyClass
x = New MyProject.MyClass()
x.MyMethod1()

なお、CreateObject関数はそのままで、変更は行なわれません。

■ 今、何をしておくべきか?

「COMコンポーネント」と「それを利用するプログラム」は両方、またはどちらか一方のみをVisual Basic .NETにアップグレードして利用できます。COMで部品化しておくことで、段階的な.NET化が可能になります。
ただし、一方ずつアップグレードする場合、Visual Basic .NETのコード(マネージドコード)とCOMのコード(アンマネージドコード)との相互運用のため、オーバーヘッドが生じてしまいます。

■ 関連・補足項目

アップグレードウィザードウィザードを利用しない場合も、COMコンポーネントを簡単に利用できます。
Visual Studio .NETの「ソリューションエクスプローラ」からプロジェクト名を右クリックして、表示されるメニュー(図1)から「参照の追加」を選択します。
「参照の追加」ダイアログの「COM」タブ(図2)から利用したいCOMコンポーネントを選択すると、必要に応じてCOMを利用するためのラッパークラスを生成してくれます。図3はこのとき表示されるメッセージです。
図1:「参照の追加」を選択 図2:利用したいCOMコンポーネントを選択
図3:ラッパークラスの生成

14. APIの利用

  • Windows APIに対応する.NET Frameworkクラスライブラリの利用(推奨)
  • 引き続き各種APIを利用することも可能

■ 変更ポイント

ほとんどの場合、Windows APIには、対応する.NET Frameworkが提供するクラスライブラリがありますので、書き換えることができます。しかし、対応するものがない場合やDLLとしてサードパーティなどから提供されるAPI関数などを使いたい場合、今までと同様にそれらを利用することもできます。

■ アップグレードウィザードによる変更点

Windows APIを利用するプログラムをアップグレードすると、可能な限り変更を行なってくれます。

・データ型などのアップグレード(Long→Integerなど)
・固定長の文字列は、VB互換文字列に変更される→そのままAPIに渡すことができる

また、変更してくれるが修正が必要なものとしては、属性情報の追加などがあります。これは必要に応じて追加してください。

・属性などの(一部)追加
(例)Ansi、Unicode キーワードを使って、文字列をどのコードで渡すかをオプションとして宣言可能

アップグレードウィザードが対応していないものとしては、

・対応する.NET Framework クラスライブラリを利用するコードへの変更
・As Any(複数のDeclare宣言などに手動で対応)
・スレッド作成、Windowsサブクラス化、メッセージキューのフックなどの操作を実行するAPI
の使用(ただし、Visual Basic .NETではランタイムエラーを発生する場合もある)

などがあります。
以上のように、.NET Frameworkを利用するように変更されるわけではないので、必要に応じて手動で変更を行なう必要があります。

■ 今、何をしておくべきか?

Windows APIの使用に関しては、Visual Basic 6.0のプロジェクトで、.NETに向けてしておくことはありません。Visual Basic .NETに移行する過程で、対応する.NET Frameworkクラスライブラリを利用するように変更しましょう。
VC++ 6.0などを利用した自作のDLLをVisual Basic 6.0から呼び出しているときなどは、そのDLLを作成するときに、すでに紹介したデータ型などを考慮しておくと移行がスムーズでしょう。

● Plus One

多くのAPIはVisual Basic 6.0と同様に使用できますが、データ型の調整が必要な場合があります。たとえば、整数型についてはアップグレードウィザードによって自動的に変更されます。
また、場合によっては、Visual Basic .NETのほうがAPIへの文字列の受け渡しを適切に行なえます。ANSIおよびUnicodeキーワードを使って、文字列をどのコードで渡すかをオプションとして宣言することができるからです。

15. エラー処理(例外処理)

  • エラー処理はそのまま利用可能
  • 新しい例外処理に変更してもよい

■ 変更ポイント

Visual Basic 6.0同様、以下のようなVisual Basic固有のエラー処理はVisual Basic .NETで
も利用することができます。

Sub Proc1
On Error GoTo ErrProc
‘ エラーの起きそうな処理
Exit Sub
ErrProc:
‘ エラー処理コード
Resume Next
End Sub

もちろん、

On Error Resume Next

も利用できます。
これらに加え、Visual Basic .NETでは以下のような「例外処理」を利用することもできます。

Sub Proc1
    Try
        ‘ エラーの起きそうな処理
    Catch
        ‘ エラー処理コード
    End Try
End Sub

例外処理は、

・見やすい
・柔軟な例外処理が可能
・ネストが可能
・例外オブジェクトの利用

などの特徴を持ちます。これにより、より高度なエラー処理が可能になります。ただし、同じプロシージャの中で、エラー処理と例外処理の両方を利用することはできません。

■ アップグレードウィザードによる変更点

Visual Basic固有のエラー処理はVisual Basic .NETでも利用できるため、アップグレードウィザードによる変更は行なわれません。

■ 今、何をしておくべきか?

エラー処理に関しては、Visual Basic 6.0のプロジェクトで特に注意することはありません。

■ 関連・補足項目

ResumeやResume Nextの機能は非常に魅力的ですが、エラー処理によってエラーが回避されない場合、エラートラップとエラー処理のループを抜け出せずに、結局アプリケーションを強制終了させなければならない状況に陥ることがあります。また、Resumeを行なうためには非常にコストがかかります。このような理由から、Visual Basic .NETでは、例外処理のほうをお勧めします。

16. レガシー機能

  • Visual Basic .NETでなくなる機能はVisual Basic 6.0でも(できるだけ)使わない

■ 変更ポイント

Visual Basic .NETにはなくなるVisual Basic 6.0の機能

・On~GoTo/GoSub、GoSub/Return
・VarPtr、ObjPtr、StrPtr
・LSetによるユーザー定義型変数の代入
・DefBool、DefByte、DefInt、DefStrなど

これらの機能を利用したことがない方は、このページは一切読む必要はありません。

■ アップグレードウィザードによる変更点

意味的に対応する処理に変更されるものもあります。たとえば、Visual Basic 6.0で
記述された以下のようなOn…GoToは、

Private Sub Proc1()
    Dim number As Integer
    number = 2
On number GoTo LineX, LineY
LineX:
    ‘ 処理
LineY:
    ‘ 処理
End Sub

アップグレードウィザードによって、次のようなSelect Caseステートメントに変換されます。

Private Sub Proc1()
    Dim number As Short
    number = 2
    Select Case number
        Case Is < 0
            Error(5)
        Case 1
            GoTo LineX
        Case 2
            GoTo LineY
    End Select
LineX:
    ‘ 処理
LineY:
    ‘ 処理
End Sub

このように、非常に冗長になっていることがおわかりいただけると思います。
また、Visual Basic 6.0で使用していた次のようなDefxxxは、

DefStr A-C ‘ AからCで始まる変数をString型で扱う     
Adata1 = “Hello”

アップグレードウィザードによって次のように変換されます。

Dim Adata1 As String
Adata1 = “Hello”

変換されるからといって、Visual Basic 6.0でのこれらの使用をお勧めするわけではありません。
また、これ以外のレガシー機能は、ウィザードによっても変更されず、そのままではビルドエラーになります。

■ 今、何をしておくべきか?

このようなレガシーな機能は、できる限りVisual Basic 6.0でも使用を避けるようにしてください。
たとえば、On…GoToを使用する代わりに、Visual Basic 6.0でもあらかじめ、適切なSelect…Caseを利用しておきましょう。また、Defxxxステートメントを使うのは避け、変数を明示的に宣言するようにしましょう。
これにより、Visual Basic .NETへのスムーズな移行が行なえます。