|
|
連載
改訂版
プロフェッショナルVB.NETプログラミング
Chapter 02 データ型の変化
株式会社ピーデー
川俣 晶
2004/03/04 |
|
|
列挙型はVB 6にも存在する機能だが、VB.NETでは挙動が変わっている。それを確かめるために、サンプル・プログラムを作成してみた。まず、VB 6で記述した例を見てみよう。
1: Enum SampleEnumType
2: alpha = 0
3: beta = 1
4: End Enum
5:
6: Private Sub dump(ByVal e As SampleEnumType)
7: Debug.Print e
8: End Sub
9:
10: Private Sub Form_Load()
11: dump SampleEnumType.alpha
12: dump SampleEnumType.beta
13: End Sub
|
|
リスト2-63 列挙型を使用したプログラム
|
これを実行すると次のようになる。
|
リスト2-64 リスト2-63の実行結果
|
それではVB.NET用に書き換えてみよう。リスト2-65がそれだ。
1: Enum SampleEnumType
2: alpha = 0
3: beta = 1
4: End Enum
5:
6: Private Sub dump(ByVal e As SampleEnumType)
7: Trace.WriteLine(e)
8: End Sub
9:
10: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
11: dump(SampleEnumType.alpha)
12: dump(SampleEnumType.beta)
13: End Sub
|
|
リスト2-65 リスト2-63をVB.NETで書き換えたプログラム
|
これを実行すると以下のようになる。
|
リスト2-66 リスト2-65の実行結果
|
見てのとおり、VB 6では数値が表示されたが、VB.NETでは名前が表示された。VB.NETでも、内部的に列挙型は数値で処理されることに変わりはない。しかし、文字列に変換される際に、数値ではなく名前を返すような動作を行うようになっている。明示的にこの動作を行う場合は、ToString()メソッドを呼び出す。VB 6のように、数値を取り出すには、以下のようにdumpメソッドを書き換える。
6: Private Sub dump(ByVal e As SampleEnumType)
7: Trace.WriteLine(CInt(e))
8: End Sub
|
|
リスト2-67 列挙型の値を数値で表示するように書き換えたdumpメソッド
|
このように、CInt関数でキャストして整数として扱えば、数値として出力される(キャストに関しては、Option Strict(本連載Chapter 13で公開予定)を参照)。
このほか、いくつかの列挙型に関するトピックを見てみよう。
1: Enum SampleEnumType As Byte
2: alpha = 0
3: beta = 1
4: End Enum
5:
6: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
7: Dim s As String
8: For Each s In System.Enum.GetNames(GetType(SampleEnumType))
9: Trace.WriteLine(s)
10: Next
11: End Sub
|
|
リスト2-68 列挙型に含まれる名前の一覧を表示するプログラム
|
これを実行すると以下のようになる。
|
リスト2-69 リスト2-68の実行結果
|
リスト2-68で注目すべき点は2つある。1つは、1行目に付加されたAs Byteというデータ型指定である。これは列挙型の値を格納する整数のデータ型を指定するものである。この例なら、たかだか0と1を示すだけでよいので、Byte型で十分である。Integer型などの、より大きな表現力を持つ整数型を使う必要はない。これにより、この列挙型の配列などを確保したとき、メモリの節約になる。これは、Integer型は32bitで記憶するが、Byte型は8bitで記憶するためである。もう1つは、8行目にあるSystem.Enumクラスのインスタンスを活用していることである。このクラスには、列挙型を活用するための機能が含まれている。GetNamesメソッドは、列挙型に含まれる名前の一覧を、文字列の配列として返す機能を持っているものである。
VB 6のString型は内容を書き換えられる。しかし、VB.NETのString型は内容を書き換えられない。VB.NETでは、特別な状況を除き、文字列の加工は別の文字列型オブジェクトを作成することを意味する。しかし、同じ文字列を異なる変数に代入するような場合、String型は参照型なので、文字列オブジェクトへの参照のみが代入され、文字列が変数にコピーされるわけではない(参照型については値型と参照型のパフォーマンスの相違、値型と参照型の振る舞いの相違を参照)。そこで、同じ文字列を大量に扱う場合、VB 6とVB.NETではメモリの必要量に相違が発生する。それを確かめるために、フォームにボタンを貼り付けてから、ボタンのクリックイベントに同じ文字列を大量に複製するコードを書き込んでみよう。まずはVB 6用から。
1: Private a(99999) As String
2:
3: Private Sub Command1_Click()
4: Dim i As Long
5: For i = 0 To 99999
6: a(i) = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
7: Next
8: End Sub
|
|
リスト2-70 ボタンがクリックされると大量の文字列をコピーするプログラム
|
このプログラムからEXEファイルを作成し、実行してみる。そして、タスクマネージャの[プロセス]タブを利用して、メモリ使用量を調べる(Windowsのバージョンによっては使用できない場合がある)。筆者が試したときは、5584KBytes(約5.5MBytes)であった。そしてボタンを押して処理を実行させてからメモリ使用量を見ると、20520KBytes(約20MBytes)になった。つまり、この処理により、20520 - 5584 = 14936KBytes(約14.5MBytes)のメモリ使用量が増えたことになる。
では、次にVB.NETの場合を見てみよう。
1: Private a(99999) As String
2:
3: Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
4: Dim i As Integer
5: For i = 0 To 99999
6: a(i) = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
7: Next
8:
9: End Sub
|
|
リスト2-71 リスト2-70をVB.NETで書き換えたプログラム
|
このプログラムをDebugビルドで実行してみた。同じように消費量を調べたところ、13612 - 13508 = 104KBytesということになった。VB 6の場合は14936KBytesだったので、けた違いに少ないことになる。ここで使用した文字列は62文字の長さなので、これを10000個の配列に入れると、文字だけで、62文字 * 10000個 * 2Bytes = 1240000Bytes = 12400KBytes(約12MBytes)必要となる(2Bytesは、文字がUnicodeのUTF-16で保存されることによるもの。これらの文字は1文字2Bytesになる)。VB 6は、ほぼこれぐらいのメモリを消費したが、VB.NETで記述した場合にはこれよりはるかに少ない量しか消費していない。これは、文字列本体は1個しか確保されず、代入はすべて参照の代入として処理されていることによる。これにより、メモリの消費量が変わるだけでなく、処理速度にも影響を及ぼす場合があるだろう。文字列を大量に扱う場合には知っておくとメリットがある特徴である。
VB 6には、「Def」の3文字で始まるステートメントがあり、変数のデフォルトのデータ型を指定することができる。例えばDefIntは、変数のデフォルトのデータ型が整数型(Integer)であることを示す。「DefInt A」と記述したソース上で、文字「A」で始まる変数名を使用すると、それはIntegerであると見なされる。かつて筆者が使っていた1979年発売のパソコン、PC-8001に搭載されていたMicrosoft製のN-BASICにもあったものだから、非常に古く伝統ある由緒正しい機能といえる。リスト2-72は、それを実際に記述したサンプル・ソースである。
1: DefInt A
2: DefSng B
3:
4: Private Sub Form_Load()
5: a = 1.2
6: b = 1.2
7: Debug.Print a, b
8: End Sub
|
|
リスト2-72 “Def〜”のステートメントを使用したプログラム
|
これを実行すると以下のような結果が得られる。
|
リスト2-73 リスト2-72の実行結果
|
5行目で初めて出現する変数aは、1行目のDefInt Aの効能により、Integer型と見なされる。そのため、1.2を代入しても整数に丸められ、1という値が格納される。一方、6行目の変数bは、2行目のDefSngの効能によりSingle型と見なされ、小数点以下の値も保存される。
これに相当するソースは、VB.NETでは記述できない。というのは、Defで始まるDefIntなどのステートメントは一切存在しないためである。無理に書けばリスト2-74のようになる。
1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2: Dim a As Integer, b As Single
3: a = 1.2
4: b = 1.2
5: Trace.WriteLine(a & ", " & b)
6: End Sub
|
|
リスト2-74 リスト2-72に似せたVB.NETのプログラム
|
これを実行すると以下のようになる。
|
リスト2-75 リスト2-74の実行結果
|
見てのとおり、変数ごとにデータ型を明示するように記述するよりほかに方法はない。しかし、この世の中で、先頭の1文字が共通なら同じデータ型などという都合のよいソースがそうそうあるわけがなく、Defで始まるステートメントは現実には使えない機能だったと考えた方がよいだろう。これはあくまで、可読性を落としてもソースを短くしなければならなかった初期のパソコンでのみ利用価値のあった機能といえる。事実、むかしの8bitパソコンでは、処理速度の向上と省メモリのために、プログラム先頭で「DEFINT A-Z」などと記述することも多かった。だが、それはすでに過去の話である。
『VB6プログラマーのための入門 Visual Basic .NET 独習講座』
本記事は、(株)技術評論社が発行する書籍『VB6 プログラマーのための 入門 Visual Basic .NET 独習講座』から許可を得て転載したものです。
【本連載と書籍の関係について 】
この書籍は、本フォーラムで連載した「連載 プロフェッショナルVB.NETプログラミング」を大幅に加筆修正し、発行されたものです。技術評論社、および著者である川俣晶氏のご好意により、書籍の内容を本フォーラムの連載記事として掲載させていただけることになりました。
→技術評論社の解説ページ
ご注文はこちらから
|