連載

プロフェッショナルVB.NETプログラミング
―― VB 6プログラマーのためのVB.NET入門 ――

第31回 残されたいくつかのトピック(その2)

(株)ピーデー
川俣 晶
2003/01/11

Page1 Page2 Page3

値型と参照型

 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
エイリアス関係にある型名と構造体のフルネーム
 

 INDEX
  連載 プロフェッショナルVB.NETプログラミング
  第31回 残されたいくつかのトピック(その2)
  1.値型と参照型/文字列の振る舞いの違い/プリミティブ型のエイリアス
    2.列挙型とその値/読み取り専用変数/複合代入ステートメント/条件付きメソッド
    3.マルチスレッドを同期するSyncLockステートメント
 
「プロフェッショナル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メールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間