連載

改訂版
プロフェッショナルVB.NETプログラミング

Chapter 03 ステートメントの変化

株式会社ピーデー 川俣 晶
2004/03/17


 本記事は、(株)技術評論社が発行する書籍『VB6プログラマーのための入門 Visual Basic .NET 独習講座』の一部分を許可を得て転載したものです。同書籍に関する詳しい情報については、本記事の最後に掲載しています。

 読み取り専用変数

 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 読み取り専用の変数を使用したプログラム

 これを実行すると以下のようになる。ただし、最後の数値は、フォームが表示された座標位置によって値が変化する。

1: 123
2: 456
3: 22
リスト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 複合代入ステートメントを使用したプログラム

 これを実行すると以下のようになる。

1: 5
リスト3-18 リスト3-17の実行結果

 例えば、ソース5行目の「a *= 4」は「a = a * 4」と同じことである。同様に、6行目の「a /= 2」は「a = a / 2」と同じことである。この機能は、^、*、/、\、+、-、&の演算子で利用できる。

 Staticの付いたプロシージャ

 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キーワードを付けたプログラム

 これを実行すると以下のようになる。

1:  1
2:  2
3:  3
リスト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のプログラム

 これを実行すると以下のようになる。

1: 1
2: 2
3: 3
リスト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を関数に付けたプログラム

 これを実行すると以下のようになる。

1: 1
2: 1
3: 1
リスト3-24 リスト3-23の実行結果

 このように関数の宣言にSharedキーワードを付けることで、ローカル変数に静的な効能が付与されるわけではない。

 ランダム・ファイルとVBFixedString属性

 文字列の固定長を指定する属性である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属性で指定された長さで文字列を取得している。


 INDEX
  [連載] 改訂版 プロフェッショナルVB.NETプログラミング
  Chapter 03 ステートメントの変化
    1.WendをEnd Whileに書き換える/プロシージャ脱出とReturnステートメント/関数の戻り値とReturnステートメント
  2.読み取り専用変数/複合代入ステートメント/ Staticの付いたプロシージャ/ランダム・ファイルとVBFixedString属性
    3.ステートメントを用いたテキスト・ファイルの入出力/ファイルシステム・オブジェクトを用いたテキスト・ファイルの入出力
    4.参照を使ったファイルシステム・オブジェクトの利用/ .NET Frameworkクラス・ライブラリを用いたテキスト・ファイルの入出力
    5.エンコーディング名を指定したテキスト・ファイルの入出力/シフトJISとUTF-8の相違
    6.UTF-8によるファイル出力時の文字列サイズの変化/テキスト・ファイルの書き込み位置/さまざまなファイル入出力手段の中でどれを選ぶべきか
    7.Debug.Assertメソッドの移行
    8.TraceクラスとDebugクラスの相違/条件式抜きで必ず停止させるFailメソッド/マルチスレッドを同期するSyncLockステートメント/古い制御構造 On…GoToとOn…GoSub
 
「改訂版 プロフェッショナルVB.NETプログラミング 」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

業務アプリInsider 記事ランキング

本日 月間
ソリューションFLASH