見出し画像

[VB.NET] 型変換/キャストのまとめ ―どう使い分ければいいのか?

明示的な変換(キャスト)

キャスト演算子
C# のキャスト演算子 () に相当するものは VB.NET にはない。

DirectCast 演算子
・Object 型とほかの型との変換において、VBランタイムヘルパーを使用しない分、CType 関数より高速に動作する。
・値型と Object 型の間の変換にはボックス化、ボックス化解除が使われる。異なる型へはボックス化解除できず、暗黙の型互換があっても InvalidCastException が発生する。
・実行時の型が変更後の型と一致しないとキャストに失敗する。たとえば、Integer 型から String 型へは変換できない。
・C# のキャスト演算子 () と異なり、暗黙の型互換があっても変換できない。たとえば、Integer 型から Long 型への変換はコンパイルエラーとなる。

TryCast 演算子
・参照型のみ。
・C# の as 演算子に相当。IL の isinst 命令 にコンパイルされる。
・TypeOf ... Is ... で判定してから DirectCast するのは変換が二度生じて無駄である。TryCast してから IsNot Nothing 判定する方がパフォーマンスがよい。

改善前

If TypeOf obj Is T Then
   Dim casted = DirectCast(obj, T)
    :
End If

改善後

Dim casted = TryCast(obj, T)
If casted IsNot Nothing Then
    :
End If

余談

IsNot Nothing に関し、『コーディング規則 - Visual Basic | Microsoft Docs』に

IsNot の代わりに Not...Is Nothing キーワードを使用します。

とありますが、誤訳ですのでご注意ください
わざわざ VB2005 で追加したわけだし、「IsNot 演算子の方が否定対象が明確でいいのになあ」と思い、念のため英語ページをあたったところ、

Use the IsNot keyword instead of Not...Is Nothing.

と全く逆になっていました。

フィードバックすべきか、と思ったらクローズが1件あったので、開いてみると、@tfukumori さんが別の箇所(Handles と AddHandler)に関して同様の指摘をされていて、なんと、

現在、機械翻訳されたコンテンツに対する翻訳の問題へのコントリビューションはできません。

という回答で終わっていました。
こんな初歩的・根本的な誤りを放置されるとは…やはり機械翻訳は参考程度に見た方がいいですね。

CType 関数
・Object 型とほかの型との変換において、Microsoft.VisualBasic.CompilerServices.Conversions クラス のようなVBランタイムヘルパーを使用する分、DirectCast よりパフォーマンスは落ちる。
・コンパイラによってインライン展開されるため、VBランタイムヘルパーが使用されない場合のパフォーマンスはよい。
・暗黙の型互換があれば変換できる。暗黙の型互換がない場合、たとえば Integer 型から DateTime 型への変換はコンパイルエラー、Object 型にボックス化された Integer 値から DateTime 型への変換は実行時に InvalidCastException となる。
・VB.NET の CType 関数と C# のキャスト演算子とでは、結果が異なる場合がある。たとえば Single 型から Integer 型に変換する場合、CType 関数では小数点以下を銀行型丸め(近い方の整数に、半整数 .5 は偶数になるように丸める)によって取り除くが、C# では切り捨てられる。

データ型変換関数(CStr, CInt, ...)
・CInt(obj) と CType(obj , Integer) は等価(ほかの型についても同様)。
・CInt の小数→整数は銀行型丸め。

ヘルパークラス/メソッドによる変換

Parse, ParseExact, TryParse, TryParseExact メソッド
・文字列から変換。
・DateTime 型の場合、ParseExact で書式を明示指定して変換することができる。
・Nothing を渡すと ArgumentNullException が発生する。

ToString メソッド
・文字列化。
・Enum の ToString() はメンバ名になる。
・Decimal の ToString で末尾のゼロを取り除く(1.0 → "1")には、書式 "G29" を指定する。(.NET 1.1 以上の場合)

Convert クラス
・基本データ型を別の基本データ型に変換する。
・内部的に Parse メソッドを呼んでいる。
・小数から整数への変換は銀行型丸め。

TypeConverter クラス
・コントロールのデザインプロパティの文字列解析などに使用されている。
TypeDescriptor.GetConverter(Type) メソッドで、実行時に型(Type オブジェクト)を指定して TypeConverter オブジェクトを取得することができる。
・文字列からの変換に ConvertFromString(String) メソッドが用意されているが、既定で用意されたコンバーターの許容範囲は狭い。桁区切りカンマ、通貨記号、全角は数値の構成要素として解釈されず、小数から整数へのキャストもできない。

Microsoft.VisualBasic.Conversion.Int メソッド
・負の値で切り捨てにならない。負でも切り捨てるには Fix 関数を使う。

Microsoft.VisualBasic.Conversion.Val メソッド
・VB6時代から想定外の挙動で何かと問題を引き起こしてきた。Val("1,234") の結果は Double の 1 である。
・手軽で許容範囲が広い反面、意図しない変換結果になることがあるので、使用には注意が必要。

暗黙的な変換

暗黙の数値変換(.NET 共通)
・暗黙的に変換可能な型の対応が「.NET の型変換の表 | Microsoft Docs」に拡大変換/縮小変換と分けて記載されている。
・拡大変換の多くは情報を失うことなく実行できるが、浮動小数点数値型への変換では精度が損なわれるケースもある。
・縮小変換では情報が失われる可能性がある。変換元の値が変換先の型の MaxValue と MinValue の範囲外にある場合は OverflowException が発生する。
・Integer のリテラル、定数値が変換先の型の範囲内にある場合、暗黙的に変換できる。範囲外の場合はコンパイルエラーとなる。

Option Strict Off
・Off だと意図しない型変換が暗黙的に行われ、バグの温床になる。On にすることが推奨されている。
・既定は Off なので、プロジェクトを作成したら [コンパイル オプション] 設定で On にするのを忘れないようにする。たまに忘れて後で気づく。VB6からの移行を考慮してのことなのかもしれないが、そろそろプロジェクトの既定は On に変えてほしい。

この記事が気に入ったらサポートをしてみませんか?