|   | 
  | 
連載
改訂版 
プロフェッショナルVB.NETプログラミング
Chapter 03 ステートメントの変化 
株式会社ピーデー
川俣 晶 
2004/03/17 | 
 | 
 
 | 
 VB.NETでは、変数を宣言する際に、ReadOnlyキーワードを付けることで、読み取り専用の変数を宣言することができる。これに相当する機能はVB 6にはない。リスト3-15はそれを利用したサンプル・プログラムである。
 1: Public Class A 
 2:   Public Const C = 123 
 3:   Public ReadOnly R1 As Integer = 456 
 4:   Public ReadOnly R2 As Integer 
 5:   Public Sub New(ByVal initialValue As Integer) 
 6:     R2 = initialValue 
 7:   End Sub 
 8: End Class 
 9:  
10: Public Class Form1 
11:   Inherits System.Windows.Forms.Form 
12:  
13: …Windows フォーム デザイナで生成されたコード… 
14:  
15:   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
16:     Dim n As New A(Me.Left) 
17:     Trace.WriteLine(n.C) 
18:     Trace.WriteLine(n.R1) 
19:     Trace.WriteLine(n.R2) 
20:   End Sub 
21: End Class 
 | 
 
 
 | 
 
  
リスト3-15 読み取り専用の変数を使用したプログラム
 | 
 これを実行すると以下のようになる。ただし、最後の数値は、フォームが表示された座標位置によって値が変化する。
| 
 | 
 
  
リスト3-16 リスト3-15の実行結果
 | 
 読み取り専用の変数は、初期化以外のタイミングで値を書き換えることはできない。初期化のタイミングとは、3行目のような宣言に付加された初期化構文や、6行目のようなコンストラクタの内部である。問題は、値を指定したら書き換えられないとすれば、2行目のような定数の宣言と何が違うのかという点である。これは、4行目で宣言されたR2の値を見れば分かるだろう。R2の値は、16行目のMe.Left、つまりフォームの左側の座標値で初期化される。これは、起動する状況によって値が変わるもので、一定しているわけではない。つまり、定数は常に値が同じだが、読み取り専用の変数は実行するごとに違う値を取ることができるわけである。
 変数aに対して「a = a + b」と記述して値bを足すのはよくあることである。このような場合、VB.NETでは「a += b」という短い記述方法(複合代入ステートメント)を使うことができる。リスト3-17はそれを利用したサンプル・プログラムである。
1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
2:   Dim a As Integer 
3:   a = 1 
4:   a += 2 
5:   a *= 4 
6:   a /= 2 
7:   a -= 1 
8:   Trace.WriteLine(a) 
9: End Sub 
 | 
 
 
 | 
 
  
リスト3-17 複合代入ステートメントを使用したプログラム
 | 
 これを実行すると以下のようになる。
| 
 | 
 
  
リスト3-18 リスト3-17の実行結果
 | 
 例えば、ソース5行目の「a *= 4」は「a = a * 4」と同じことである。同様に、6行目の「a /= 2」は「a = a / 2」と同じことである。この機能は、^、*、/、\、+、-、&の演算子で利用できる。
 VB 6では、プロシージャの宣言にStaticキーワードを付けることができる。すると、プロシージャ内で宣言された変数に、Staticキーワードが付いたかのような効能を与えることができる。リスト3-19はそれを実際に記述してみた例である。
 1: Private Static Sub test() 
 2:   Dim a As Integer 
 3:   a = a + 1 
 4:   Debug.Print a 
 5: End Sub 
 6:  
 7: Private Sub Form_Load() 
 8:   test 
 9:   test 
10:   test 
11: End Sub 
 | 
 
 
 | 
 
  
リスト3-19 プロシージャの宣言にStaticキーワードを付けたプログラム
 | 
 これを実行すると以下のようになる。
| 
 | 
 
  
リスト3-20 リスト3-19の実行結果
 | 
 ソース2行目の変数aは、Staticの効能が及ぶため、プロシージャを抜けても値が保存され、再び呼び出されると以前の値を使うことができる。
 しかし、VB.NETではこの構文が使えない。Staticキーワードは、Staticの効能を必要とするすべての変数で記述しなければならない。リスト3-21はそのようにして書き直したVB.NETのプログラムである。
 1: Private Sub test() 
 2:   Static a As Integer 
 3:   a = a + 1 
 4:   Trace.WriteLine(a) 
 5: End Sub 
 6:  
 7: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
 8:   test() 
 9:   test() 
10:   test() 
11: End Sub 
 | 
 
 
 | 
 
  
リスト3-21 Static変数を用いてリスト3-19を書き換えたVB.NETのプログラム
 | 
 これを実行すると以下のようになる。
| 
 | 
 
  
リスト3-22 リスト3-21の実行結果
 | 
 コード2行目でStaticキーワードを使って変数を宣言していることが読みとれると思う。多少手間は掛かるが、プログラマーの意図がソース上から読みとりやすくなった。
 なお、余談だが、VBだけでなくJava/C#も使える二刀流のプログラマーは、Staticの意味がそれらの言語と異なっていることに注意しておく必要がある。VBのStaticキーワードは、メソッドの呼び出し間で値を維持する静的ローカル変数のために使われるが、Java/C#のstaticキーワードは、インスタンスに属さずクラスに属するメンバを宣言するために使用される。
 Java/C#のstaticに相当するVB.NETでのキーワードはSharedである。これは、関数に付加することができるが、VB 6のStaticキーワードとは意味が違うので、代用品にはならない(インスタンスを作成せずに呼び出せるメソッドを参照)。実際に、リスト3-23のようなプログラムを記述してみれば明らかだろう。
 1: Private Shared Sub test() 
 2:   Dim a As Integer 
 3:   a = a + 1 
 4:   Trace.WriteLine(a) 
 5: End Sub 
 6:  
 7: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
 8:   test() 
 9:   test() 
10:   test() 
11: End Sub 
 | 
 
 
 | 
 
  
リスト3-23 Staticの代わりにSharedを関数に付けたプログラム
 | 
 これを実行すると以下のようになる。
| 
 | 
 
  
リスト3-24 リスト3-23の実行結果
 | 
 このように関数の宣言にSharedキーワードを付けることで、ローカル変数に静的な効能が付与されるわけではない。
 文字列の固定長を指定する属性であるVBFixedString属性が文字列変数の長さを制限しないことは、固定長文字列で解説している(属性については、属性(本連載Chapter 14で公開予定)を参照)。では、いったいどこでこの長さは意味を持つのか。その実例を示すサンプルをお見せしよう。また、使用するステートメントが、VB.NET専用クラス・ライブラリの呼び出しに変化している点も注意して見ていただきたい。
 リスト3-25は、VB 6で記述したランダム・ファイルを扱うサンプル・ソースである。
 1: Private Type Person 
 2:    Age As Integer 
 3:    Name As String * 32 
 4: End Type 
 5:  
 6: Private Sub Form_Load() 
 7:   Dim p As Person 
 8:   Open "c:\test.dat" For Random As #1 Len = Len(p) 
 9:   p.Age = 17 
10:   p.Name = "太郎" 
11:   Put #1, 1, p 
12:   p.Age = 18 
13:   p.Name = "花子" 
14:   Put #1, 2, p 
15:  
16:   Get #1, 1, p 
17:   Debug.Print p.Age 
18:   Debug.Print p.Name 
19:   Get #1, 2, p 
20:   Debug.Print p.Age 
21:   Debug.Print p.Name 
22:   Close #1 
23: End Sub 
 | 
 
 
 | 
 
  
リスト3-25 固定長のデータをファイルに書き込み、それを読み込むプログラム
 | 
 これを実行すると次のようになる。
-17- 
太郎------------------------------ 
-18- 
花子------------------------------ 
 | 
 
 
 | 
 
  
リスト3-26 リスト3-25の実行結果
 | 
| 
 ※注 リスト中のハイフン(-)は、ここでは半角スペースを示しています。 
 | 
 Typeステートメントで定義した固定長のデータをPutステートメントで書き込み、Getステートメントで読み出している。これをVB.NET用に書き換えたのが、リスト3-27である。
 1: Public Class Form1 
 2:   Inherits System.Windows.Forms.Form 
 3:  
 4: …Windows フォーム デザイナで生成されたコード… 
 5:  
 6:   Private Structure Person 
 7:     Public Age As Integer 
 8:     <VBFixedStringAttribute(32)> Public Name As String 
 9:   End Structure 
10:  
11:   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
12:     Dim p As Person 
13:     FileOpen(1, "c:\test.dat", OpenMode.Random, , , Len(p)) 
14:     p.Age = 17 
15:     p.Name = "太郎" 
16:     FilePut(1, p, 1) 
17:     p.Age = 18 
18:     p.Name = "花子" 
19:     FilePut(1, p, 2) 
20:  
21:     FileGet(1, p, 1) 
22:     Trace.WriteLine(p.Age) 
23:     Trace.WriteLine(p.Name) 
24:     FileGet(1, p, 2) 
25:     Trace.WriteLine(p.Age) 
26:     Trace.WriteLine(p.Name) 
27:     FileClose(1) 
28:   End Sub 
29: End Class 
 | 
 
 
 | 
 
  
リスト3-27 リスト3-25をVB.NETで書き換えたプログラム
 | 
 これを実行すると次のようになる。 
17 
太郎---------------------------- 
18 
花子---------------------------- 
 | 
 
 
 | 
 
  
リスト3-28 リスト3-27の実行結果
 | 
| 
 ※注 リスト中のハイフン(-)は、ここでは半角スペースを示しています。 
 | 
 入出力に使用するステートメントは、VB.NET専用クラス・ライブラリに含まれる関数の呼び出しに置き換えられている。Openステートメントの代わりにFileOpen関数、Putステートメントの代わりにFilePut関数、Getステートメントの代わりにFileGet関数、Closeステートメントの代わりにFileClose関数がそれぞれ使われている。書式が少し違うが、渡している引数の内容はほぼ同じである。FileOpen関数に渡しているいくつかの名前が異なっているが、基本的には同じ意味を持つキーワードが存在している。
 さて、肝心の8行目に記述されたVBFixedString属性の効能だが、13行目のLen関数、16、19行目のFilePut関数、21、24行目のFileGet関数の動作に影響を及ぼしている。Len関数は、文字列の長さに関係なく、VBFixedString属性の値を用いて構造体の長さを計算しており、ランダム入出力するための正しい構造体のバイト数を計算できる。また、FilePut関数では、VBFixedString属性で指定された長さに足りない文字列には空白文字を補って、正しい長さでファイルに書き込みを行う。FileGet関数では、VBFixedString属性で指定された長さで文字列を取得している。