|
|
連載
プロフェッショナルVB.NETプログラミング
―― VB 6プログラマーのためのVB.NET入門 ――
第31回 残されたいくつかのトピック(その2)
(株)ピーデー
川俣 晶
2003/01/11
|
値型と参照型
VB.NET(Visual Basic .NET)における値型と参照型の相違についてはすでに触れているが、勘違いすると、とんでもないバグを生み出してしまうこともあるので、もう一度違う角度から解説しよう。以下は、クラスと構造体を同じように操作するサンプル・プログラムである。
1: Public Class C
2: Public n As Integer
3: End Class
4:
5: Public Structure S
6: Public n As Integer
7: End Structure
8:
9: Public Class Form1
10: Inherits System.Windows.Forms.Form
11:
12: …Windows フォーム デザイナで生成されたコード…
13:
14: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
15: Dim c1 As New C(), c2 As New C()
16: Dim s1 As S, s2 As S
17:
18: c1.n = 123
19: c2 = c1
20: c1.n = 456
21: Console.WriteLine(c1.n)
22: Console.WriteLine(c2.n)
23:
24: s1.n = 123
25: s2 = s1
26: s1.n = 456
27: Console.WriteLine(s1.n)
28: Console.WriteLine(s2.n)
29: End Sub
30: End Class
|
|
クラスと構造体を同じように操作するサンプル・プログラム1 |
これを実行すると以下のような結果になる。
1: 456
2: 456
3: 456
4: 123
|
|
サンプル・プログラム1の実行結果 |
見てのとおり、クラスの変数を操作したときと、構造体の変数を操作したときでは結果が同じにならない。その理由は、値型と参照型の相違にある。まず、ソース18〜20行目の処理は、クラスのインスタンスを対象としたものである。クラスは参照型である。そのため、19行目の代入文は、参照の代入として処理される。その結果、20行目を実行するとき、変数c1とc2は同じインスタンスを参照していることになる。つまり、c1.nとc2.nは同じ変数を指し示している。その結果、c1.nへの代入は、c2.nの値も変化させる。一方、24〜26行目は値型の構造体を扱っている。値型は、代入するときに、値の内容そのものをコピーする。そのため、25行目の代入は、構造体の内部にある情報の複製を作ることになる。その結果、26行目の代入でs1.nは書き変わるが、s2.nはそれとは独立しているので値は変化しない。
もう1点注意する必要があるのが、15〜16行目の宣言である。参照型は参照するインスタンスを明示的にNewキーワードで生成する必要がある。しかし、値型はNewキーワードを使わなくても、そのまま使用できることが分かるだろう。
VB.NETでは、構造体のほか、Integerなどの数値型も値型である。それに対して、クラスのほか、文字列などは参照型である。次の項目で述べる文字列の振る舞いの違いで、文字列を代入しても使用メモリ容量がそれほど増えていないのは、文字列が参照型であり、代入時には参照しか代入されないことによる。
文字列の振る舞いの違い
VB 6(Visual Basic 6.0)のString型は内容を書き換えられる。しかし、VB.NETのString型は内容を書き換えられない。VB.NETでは、文字列の加工は、特別な状況を除き、別の文字列型オブジェクトを作成することを意味する。しかし、同じ文字列を異なる変数に代入するような場合、文字列オブジェクトへの参照のみが代入され、文字列が変数にコピーされるわけではない。そこで、同じ文字列を大量に扱う場合、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
|
|
ボタンがクリックされると大量の文字列をコピーするVB 6のサンプル・プログラム2 |
このプログラムからEXEファイルを作成し、実行してみる。そして、タスク・マネージャの[プロセス]タブを利用して、メモリ使用量を調べる。筆者が試したときは、5584Kbyte(約5.5Mbytes)であった。そしてボタンを押して処理を実行させてからメモリ使用量を見ると、20520Kbyte(約20Mbytes)になった。つまり、この処理により、20520-5584=14936Kbyte(約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
|
|
ボタンがクリックされると大量の文字列をコピーするVB.NETのサンプル・プログラム3 |
このプログラムをDebugビルドで実行してみた。同じように消費量を調べたところ、13612-13508=104Kbyteということになった。VB 6の場合は14936Kbyteだったのでけた違いに少ないことになる。ここで使用した文字列は62文字の長さなので、これを10000個の配列に入れると、文字だけで、62*10000*2=1240000byte=12400Kbyte(約12Mbytes)必要となる。VB 6はほぼこれぐらいのメモリを消費したが、VB.NETで記述した場合にはこれよりはるかに少ない量しか消費していない。これは、文字列本体は1個しか確保されず、代入はすべて参照の代入として処理されていることによる。これにより、メモリの消費量が変わるだけでなく、処理速度にも影響を及ぼす場合があるだろう。文字列を大量に扱う場合には知っておくとメリットがある特徴である。
プリミティブ型のエイリアス
VB.NETでは、Integerというデータ型は、System.Int32構造体のエイリアス(別名)であるとされている。従ってIntegerの代わりにSystem.Int32と記述しても、まったく同等である。実際にこの2つを使い比べてみるサンプル・プログラムを記述してみた。
1: Public Class Form1
2: Inherits System.Windows.Forms.Form
3:
4: …Windows フォーム デザイナで生成されたコード…
5:
6: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
7: Dim i1 As Integer
8: Dim i2 As System.Int32
9:
10: i1 = 123
11: Trace.WriteLine(i1)
12: Trace.WriteLine(i1.GetType().FullName)
13:
14: i2 = 123
15: Trace.WriteLine(i2)
16: Trace.WriteLine(i2.GetType().FullName)
17: End Sub
18: End Class
|
|
IntegerとSystem.Int32を使い比べたサンプル・プログラム4 |
これを実行すると以下のようになる。
1: 123
2: System.Int32
3: 123
4: System.Int32
|
|
サンプル・プログラム4の実行結果 |
見てのとおり、動作に何ら差はなく、データ型のフルネームを表示させると、どちらも、System.Int32という文字列を返す。もう1つ注目すべき点は、12行目で、Integer型の値i1に対してGetTypeメソッドを呼び出している点である。GetTypeメソッドは、本来System.Int32構造体が持っているものだが、Integer型として宣言しても、System.Int32構造体のメソッドを呼び出すことができる。
IntegerとSystem.Int32のようなエイリアス関係の名前としては、ほかに以下の表に挙げたようなものがある。
別名 |
構造体のフルネーム |
Byte |
System.Byte |
Short |
System.Int16 |
Integer |
System.Int32 |
Long |
System.Int64 |
Single |
System.Single |
Double |
System.Double |
Decimal |
System.Decimal |
Boolean |
System.Boolean |
Date |
System.DateTime |
Char |
System.Char |
String |
System.String |
|
エイリアス関係にある型名と構造体のフルネーム |
Insider.NET 記事ランキング
本日
月間