いつまでも危ない(?)PDFを晒しておくわけには行かないのでw、小ネタでブログを進めます。
時々(特にVBプログラマの方?)では、文字列へのキャストを ToString で行っている箇所を見掛けるのですが、これは危ういです、という話ですね。
例えば、データベースから読み込みをした時に VB だと、こういう風に書いているのです。
Dim da as new SqlDataAdapter("SELECT * ...", cn ) Dim dt as new DataTable da.Fill(dt) ' 文字列を取り出す dim name as String = "" if dt.Rows.Count > 0 then name = dt.Rows(0)("name").ToString() end if
こんな風に、ToString メソッドを使って object 型を文字列に変換しています。
ただし、本来はここはキャストを使うべきです。後述しますが、キャストと ToString メソッドの【意味】が異なるので、必ずしも同じ動作をするとは限らないのためです。
dim name as string = CType( dt.Rows(0)("name"), String )
C# の場合は、
string name = (string)dt.Rows[0]["name"];
# 詳細に言えば、dynamic cast を使うんでしょうが、ここでは普通のキャストで。
さて、ToString でも用途は足りるので、これでも良いような気がしますが、何故キャストを使わないといけないかというと、以下の理由があります。
dim name As String = obj.ToString()
とした時に、name には必ず期待する【文字列】が入るかというと、実は異なるのです。これは、ListBox を扱うと分かるのですが、ToString メソッドはオーバーライド可能なので、単純なキャストとは異なる値を入れることができるのです。
動作が分かるように極端な例を示すと、
Public Class AClass Public Shadows Function ToString() As String Return "ToString AClass" End Function Public Shared Narrowing Operator CType(ByVal b As AClass) As String Return "Cast AClass" End Operator End Class
ToString をオーバーライドしたものと、キャストを再定義したものを用意します。
すると次のコードでは実行結果が違ってきます。
Dim a As New AClass ' ToString の場合 Debug.Print(a.ToString()) ' Cast の場合 Debug.Print(CType(a, String))
▼実行結果
ToString AClass Cast AClass
こんな風に、ToString が定義されている時は、思ったとおりには動かないのです。まぁ、こういう時は、String 型へのキャスト自体も危ういところなのですが、ひとまず ToString とキャストは違う動作をする、ってことを覚えてコーディングして欲しいなぁ、と。
余談を言えば、キャストの場合は string 型にキャストできない場合は例外を発生させるけど、ToString 型は例外にはならない(多分ならない)ですよね。このあたり、意図して ToString メソッド(ToIntegerメソッドとかと同列の意味で)を使う分には ok ってことなのです。