|
|
連載
改訂版
プロフェッショナルVB.NETプログラミング
Chapter 01 基本構文の変化
株式会社ピーデー
川俣 晶
2004/02/19 |
|
|
ソース・コードに日付時刻を直接記述する場合は、日付リテラル形式(シャープ記号(#)で文字列を囲む)で記述するのが最も基本である。また、文字列で日付時刻を記述して日付型に変換する方法もある。これに関する若干の相違を説明する。まず、VB 6で書いたサンプル・プログラムをご覧いただきたい(リスト1-9)。
1: Private Sub Form_Load()
2: Dim d1 As Date, d2 As Date
3: d1 = "2002/01/02"
4: d2 = #1/2/2002#
5: Debug.Print d1 = d2
6: Dim t1 As Date, t2 As Date
7: t1 = "01:02:03"
8: t2 = #1:02:03 AM#
9: Debug.Print t1 = t2
10: Dim dt1 As Date, dt2 As Date
11: dt1 = "2002/01/02 01:02:03"
12: dt2 = #1/2/2002 1:02:03 AM#
13: Debug.Print t1 = t2
14: End Sub
|
|
リスト1-9 日付リテラルと文字列を比較するプログラム
|
これを実行すると以下のようになる。つまり、日付リテラルと、文字列から変換された日付の値は同じである。
|
リスト1-10 リスト1-9の実行結果
|
一見、文字列を使った方は自然に年月日の順に並んで分かりやすく、日付リテラルは月日年の順に並んで不自然に見えるかもしれない。しかし、文字列から変換する場合の書式は、システムの国際化関連の設定により変化し得るもので、あるパソコンで正しく日付型に変換できたものが、ほかのパソコンで正しく変換できる保証はない。その点で、日付リテラルは、システムの設定にかかわらず目的の日付時刻を確実に記述できるので、より安全である。さて、このソースをVB.NET上で動くようにしたのがリスト1-11である。
1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2: Dim d1 As Date, d2 As Date
3: d1 = "2002/01/02"
4: d2 = #1/2/2002#
5: Trace.WriteLine(d1 = d2)
6: Dim t1 As Date, t2 As Date
7: t1 = "01:02:03"
8: t2 = #1:02:03 AM#
9: Trace.WriteLine(t1 = t2)
10: Dim dt1 As Date, dt2 As Date
11: dt1 = "2002/01/02 01:02:03"
12: dt2 = #1/2/2002 1:02:03 AM#
13: Trace.WriteLine(t1 = t2)
14: End Sub
|
|
リスト1-11 リスト1-9をVB.NETで書き換えたプログラム
|
これを実行すると以下のようになる。
|
リスト1-12 リスト1-11の実行結果
|
日付リテラルの動作は変わっていない。VB.NETでも、やはり日付リテラルを使う方が安全といえる。その点でVB 6とVB.NETの間に相違はなく、変化はない。
しかし、ここで問題なのはソース・コードの書式の違いではない。IDEで日付リテラルを入力しているとき、VB 6では年月日の順番で入力しても、システムが自動的に正しい順番に直してくれる。例えば「#2001/01/02#」と入力すると、自動的に「#01/02/2001#」に直してくれる。しかし、VB.NETのIDEはそのようなサービスを行ってくれず、自分で正しい順序に入力しなければならない。
もし、正しい順序を正確に把握せずにVB 6で日付リテラルを使用していた場合は、VB.NETに移行する際には、正しい順序を頭に入れておく必要がある。時刻の方は24時間制で入力しても自動的に12時間制に直してくれるようなので、こちらは問題にならないだろう。
Dimステートメントで変数を宣言する際に、データ型を明示しない変数と明示する変数を同時に宣言すると、VB 6とVB.NETでは結果が異なる場合がある。以下にその例を紹介する。まずはVB 6で記述して正常に動作するサンプル・ソースを示す(リスト1-13)。
1: Private Sub Form_Load()
2: Dim a, b As Integer
3: a = 32768
4: b = 1
5: Debug.Print a + b
6: End Sub
|
|
リスト1-13 データ型を明示しない変数と明示する変数を同時に宣言しているプログラム
|
これを実行すると以下の結果を表示する。
|
リスト1-14 リスト1-13の実行結果
|
このソースでは、aはVariant型、bはInteger型になり、Integer型には入らない大きな数32768をVariant型に代入することは何の問題もない。
さて、次は同じソースをVB.NETで記述した場合である(リスト1-15)。
1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2: Dim a, b As Short
3: a = 32768
4: b = 1
5: Trace.WriteLine(a + b)
6: End Sub
|
|
リスト1-15 リスト1-13と同じ内容をVB.NETで記述したもの
|
このソースでは、数値範囲を一致させるためにIntegerをShortに置き換えている(新しい整数型と整数型が表現できる数値範囲の拡大を参照)。これをビルドすると、以下のようなコンパイルエラーになる。
Q:\aWrite\@it\VB.NET\002\smpl\sample006n\Form1.vb(47) : error BC30439: 定数式は、型 'Short' では表現できません。
|
|
リスト1-16 リスト1-15をビルドしたときのコンパイルエラー
|
エラーが起きたのは47行目と表示されているが、これはリスト1-15では3行目に当たる。3行目に記述された変数aはShort型であるため、32768を入れるには小さすぎるということがコンパイラにより指摘されたのである。2行目の変数宣言は、VB 6ではaの指定が省略されたと見なされてVariant型になるが、VB.NETではa、bの両方がShort型と見なされ、aのデータ型指定が省略されたとは見なされない。その結果、2つのソースは同じ意味を持つものとして扱われないことになる。
このようなトラブルを回避するには、できるだけ変数のデータ型を明示的に付けるように習慣付ければよいだろう。
VB 6では、メソッド内で定義された変数は、メソッド内全域で有効であった。そのため、Forループの内側で宣言した変数をForループの外側で参照しても問題はない。また、宣言された場所に関係なく、メソッドを脱出するまでその内容は有効であり、値を累積することも可能だった。このことを示すサンプル・ソース(リスト1-17)と実行結果(リスト1-18)を以下に示す。
1: Private Sub Form_Load()
2: Dim a As Integer
3: For i = 0 To 9
4: Dim b As Integer
5: a = a + 1
6: b = b + 1
7: Debug.Print a, b
8: Next
9: Debug.Print a, b
10: End Sub
|
|
リスト1-17 変数のスコープを示すプログラム
|
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
10 10
|
|
リスト1-18 リスト1-17の実行結果
|
これをVB.NETに書き換えたものを以下に示す。
1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2: Dim a As Integer
3: Dim i As Integer
4: For i = 0 To 9
5: Dim b As Integer
6: a = a + 1
7: b = b + 1
8: Trace.Write(a)
9: Trace.Write(", ")
10: Trace.WriteLine(b)
11: Next
12: Trace.Write(a)
13: Trace.Write(", ")
14: 'Trace.WriteLine(b) '名前 'b' は宣言されていません。
15: End Sub
|
|
リスト1-19 リスト1-17をVB.NETで書き換えたプログラム
|
これを実行すると以下のようになる。
1, 1
2, 2
3, 3
4, 4
5, 5
6, 6
7, 7
8, 8
9, 9
10, 10
10,
|
|
リスト1-20 リスト1-19の実行結果
|
ほとんど同じように見えるが、ソースの14行目がエラーになる点に注意が必要である。VB.NETでは、変数はスコープを持っており、Forループなどブロック構造を表記する構文の中で宣言された変数は、そのブロック構造の内側からしか利用することができない。しかし、変数にスコープを持つほかの主要な言語(C/C++/Java/C#など)とは異なり、メソッドなどを脱出するまで変数は有効という特徴は継承されており、ブロック内で宣言された変数で値を累積していくことができる。この点で、VB.NETのスコープの概念は、VB 6と異なるだけでなく、C/C++/Java/C#などとも異なるため、注意して扱う必要がある。
値を累積したくない場合は、変数宣言に、例えば「Dim a As Integer = 123」のように初期化コードを必ず書き込むようにすると、意図しない動作を防止できる。