|
|
連載
改訂版
プロフェッショナルVB.NETプログラミング
Chapter 02 データ型の変化
株式会社ピーデー
川俣 晶
2004/03/04 |
|
|
通貨、日付、構造体などのデータ型の変化を見てみよう。リスト2-10は、VB 6で記述した、いくつかのデータ型を利用するサンプル・ソースである。
1: Private Type TypeA
2: s1 As String * 2
3: s2 As String * 2
4: End Type
5:
6: Private Type TypeB
7: s As String * 4
8: End Type
9:
10: Private Sub Form_Load()
11: Dim c As Currency
12: c = 105
13: c = c * 1.05
14: Debug.Print c
15:
16: Dim d As Date
17: d = "2002/04/01"
18: Debug.Print d
19:
20: Dim a As TypeA
21: a.s1 = "AB"
22: a.s2 = "CD"
23: Dim b As TypeB
24: LSet b = a
25: Debug.Print b.s
26: End Sub
|
|
リスト2-10 いくつかのデータ型を使用したプログラム
|
これを実行すると以下のようになる。
|
リスト2-11 リスト2-10の実行結果
|
見てのとおり、Currency型、Date型を使い、Typeステートメントでユーザー定義型を定義して、LSetステートメントで異なるユーザー定義型の間で内容を代入するということを行っている。では、これに相当するソースをVB.NETで記述するとどうなるだろうか。
1: Private Structure TypeA
2: Dim s1 As String
3: Dim s2 As String
4: End Structure
5:
6: Private Structure TypeB
7: Dim s As String
8: End Structure
9:
10: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
11: Dim c As Decimal
12: c = 105
13: c = c * 1.05
14: Trace.WriteLine(c)
15:
16: Dim d As Date
17: d = "2002/04/01"
18: Trace.WriteLine(d)
19: Trace.WriteLine(d.ToString("yyyy'年'MM'月'dd'日'"))
20:
21: Dim a As TypeA
22: a.s1 = LSet("AB", 2)
23: a.s2 = LSet("CD", 2)
24: Dim b As TypeB
25: b.s = LSet(a.s1 & a.s2, 4)
26: Trace.WriteLine(b.s)
27: End Sub
|
|
リスト2-12 リスト2-10に相当するVB.NETのプログラム
|
これを実行すると以下のようになる。
110.25
2002/04/01 0:00:00
2002年04月01日
ABCD
|
|
リスト2-13 リスト2-12の実行結果
|
ここではさまざまな相違があるので、順番に見ていこう。
11行目のDecimal型は、Currency型の置き換えとして存在するデータ型だ 。Currency型はVB.NETではサポートされないので使用することはできない。それに対して、Decimal型はVisual Basic専用ではなく、ほかの.NET Framework上のプログラム言語でも使用できる共通のデータ型である。そのため、C#で作成された経理計算ライブラリをVisual Basicから呼び出して利用する、というようなシナリオもスムーズに実現できる。
なお、Currency型は -922,337,203,685,477.5808から922,337,203,685,477.5807までの範囲の値を扱う固定小数点数値である。小数部は4桁に固定されている。それに対して、Decimal型は -79,228,162,514,264,337,593,543,950,335から79,228,162,514,264,337, 593,543,950,335までの値を扱う小数点数値であるが、小数部の桁数は1つに決まっておらず、必要に応じて小数点の位置を変えていく仕組みである。しかし、浮動小数点型とは異なり、10進表記で小数点の位置を扱うことで、丸め誤差が出ないようになっている。
次は16行目のDate型である。ここでは、VBらしいラフで曖昧なデータの扱いを見せるという意味で、日付リテラル(日付リテラルについての注意点を参照)を使わず、文字列表記の日付からDate型への変換を行わせている。
さて、一見、16〜18行目は、何らVB 6から変更なく機能しているかのように見えるが、結果を見ると、VB 6は日付だけを表示しているのに対して、VB.NETでは日付と時刻を表示していることが分かる。もちろん、時刻は指定していないので「0:00:00」という内容になっているものの、表示はなされてしまっている。この相違は、VB 6のDate型が内部で保持する倍精度浮動小数点型を文字列に変換するルールと、VB.NETのDate型が内部で保持するDateTime構造体の文字列への変換ルールが異なることによって発生したものである。Date型を何も指定せずに文字列に変換させる場合、VB 6では日付と時刻を含む文字列を生成する。しかし、時刻が「00:00:00」の場合は日付のみを含む文字列を生成する。これは日付と時刻を分離して扱っていた旧世代のBasic言語との互換性のための挙動と思われる。
言語に対して中立な.NET Frameworkのクラス・ライブラリでは、このような互換のための配慮は継承されていないようで、単純に文字列に変換すると、条件に関係なく常に時刻を含む文字列に変換される。VB 6での動作をVB.NETでも正確に再現したい場合は、書式を明示的に指定する必要がある。
逆の言い方をすれば、VB.NETのDate型はDateTime構造体に相当するので、DateTime構造体が持つメソッドやプロパティを利用することもできる。例えば19行目のように、書式化された文字列に変換するToStringメソッドを呼び出して、望みどおりの書式に整形させることもできる(日付時刻の詳細に関しては、日付と時刻の変更、日付時刻の書式化も参照)。
さて、次はユーザー定義のデータ型である。まず。1行目と6行目を見ていただきたい。VB 6のTypeキーワードがVB.NETではStructureに変わっていることが分かると思う。これも、ほかのプログラム言語と共通の構造体と呼ばれる機能に整合させるためのもので、Visual Basicで定義した構造体は、ほかのプログラム言語から参照することができる。また、構造体内部の変数の宣言にDimキーワードが付加されていることも大きな変更点だ。構造体の中にはメソッドなども書き込むことができるので、Dimキーワードを付けて変数であることを明示する必要が生じたのである(構造体の詳細に関しては、構造体宣言、値型と参照型のパフォーマンスの相違、値型と参照型の振る舞いの相違も参照)。
次に、2行目などで、固定長文字列が可変長文字列の宣言に書き換えられている点に注意していただきたい。VB.NETには固定長文字列は用意されていない。文字列はすべて可変長である(文字列の長さに関する詳細は、固定長文字列を参照)。
さらに、VB.NETではLSet、RSetステートメントも存在しないため、VB 6でのサンプル(リスト2-10)の20〜24行目と等価なコードをVB.NETで記述することはできない。無理やりほぼ同じ機能を記述したのが、リスト2-12の21〜25行目である。LSet関数は、指定された長さの文字列を左ぞろえにして得る関数である。今回の例では、文字列長は2や4に決まっているのでLSet関数を使う意味はないが、サンプル・ソースとして意味を明確にするために記述してみた。
コンピュータの世界では、数を数えるときに1ではなく、0からカウントすることが多い。しかし、これは一般常識としてはなじみにくい。VB 6には、配列の添え字を0から使うか1から使うかを指定するOption Baseという機能があった。リスト2-14はそれを用いて、1から数えるようにしたサンプル・ソースである。
1: Option Base 1
2: Private Sub Form_Load()
3: Dim a(10) As Integer
4: For i = 1 To 10
5: a(i) = i * 2
6: Next
7: For i = 1 To 10
8: Debug.Print a(i)
9: Next
10: End Sub
|
|
リスト2-14 配列の添え字の始まりを1に指定したプログラム
|
これを実行すると以下のようになる。
2
4
6
8
10
12
14
16
18
20
|
|
リスト2-15 リスト2-14の実行結果
|
VB.NETには、Option Baseが存在しないため、配列の下限を1に設定することができない。そのため、リスト2-16のように下限が0であることを前提とするコードに書き換えねばならない。
1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2: Dim a(9) As Integer, i As Integer
3: For i = 0 To 9
4: a(i) = (i + 1) * 2
5: Next
6: For i = 0 To 9
7: Trace.WriteLine(a(i))
8: Next
9: End Sub
|
|
リスト2-16 リスト2-14をVB.NETで書き換えたプログラム
|
これを実行すると以下のようになる。
2
4
6
8
10
12
14
16
18
20
|
|
リスト2-17 リスト2-16の実行結果
|
これについては、楽な解決方法が何もない。地道にソースの書き方を変えていくしか対処方法がないだろう。
VB 6には、配列の大きさを変更するReDimステートメントが存在する。しかし、このステートメントは、あらかじめサイズの決まっていない配列を宣言してから、そのサイズを変更するという手順を踏まねば使用できない。リスト2-18は、配列のサイズを変えながら値を格納するサンプル・ソースである。
1: Private Sub Form_Load()
2: Dim a() As Integer
3: ReDim a(1)
4: a(0) = 0
5: a(1) = 1
6: ReDim Preserve a(2)
7: a(2) = 2
8: For i = 0 To 2
9: Debug.Print a(i)
10: Next
11: End Sub
|
|
リスト2-18 ReDimステートメントを使用したプログラム
|
これを実行すると以下のようになる。
|
リスト2-19 リスト2-18の実行結果
|
ここではReDimを使用する準備として、2行目でサイズのない配列を用意している。このサイズのない配列aに対して、3行目や6行目ではReDimステートメントを適用している。だが、VB.NETではこのあたりの挙動がやや異なる。リスト2-20は同じ機能を持つコードをVB.NETで記述してみた例である。
1: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
2: Dim i As Integer
3: Dim a(1)
4: a(0) = 0
5: a(1) = 1
6: ReDim Preserve a(2)
7: a(2) = 2
8: For i = 0 To 2
9: Trace.WriteLine(a(i))
10: Next
11: End Sub
|
|
リスト2-20 リスト2-18をVB.NETで書き換えたプログラム
|
これを実行すると以下のようになる。
|
リスト2-21 リスト2-20の実行結果
|
ここで注目すべきは3行目と6行目である。サイズのある配列に対してReDimステートメントを適用しているが、VB 6と異なり、VB.NETではこのコードは正常に機能する。VB.NETでは、すべての配列がサイズ変更可能扱いとなっている。これは、.NET Frameworkで扱われる配列の仕様と一致させるための仕様変更と思われる。
更新履歴 |
【2005/1/21】本ページの「通貨、日付、構造体の変化」の部分に以下のような誤りがありました。お詫びして訂正させていただきます。
誤 |
それに対して、Decimal型は -79,228,162,514,264,337,593,543,950,335から79,228,162,514,264,337, 593,543,950,335までの値を扱う固定小数点数値であるが、小数部の桁数は1つに決まっておらず、必要に応じて小数点の位置を変えていく仕組みのようである。 |
正 |
それに対して、Decimal型は -79,228,162,514,264,337,593,543,950,335から79,228,162,514,264,337, 593,543,950,335までの値を扱う小数点数値であるが、小数部の桁数は1つに決まっておらず、必要に応じて小数点の位置を変えていく仕組みである。 |
|
|