2013年5月30日

キャストの比較 #1 対比表

C#.NET VB.NET
(Type)value DirectCast(value, Type) 基本のキャスト。value の型と Type に継承関係がある場合にキャスト可能。
継承関係ではないが、列挙型とその基になる型の場合、相互にキャスト可能。
C#のみ、下記が可能。
  • 数値型の間で相互変換
  • 列挙型と数値型の間で相互変換(規則は数値型同士の変換と同じ)
  • operator による独自の実装
× CType(value, Type) VB 系の CInt や CStr を型指定して変換できるようにしたもの。
列挙型と数値型の変換も可能。(規則は数値型同士の変換と同じ)
C# の (Type)value と同じく Operator による独自の実装が可能。
ただし、C# の (Type)value の結果と異なることがあり(☆1)、数値型⇔文字列型の変換も可能なことから別物と考えるべき。
value as Type TryCast(value, Type) キャスト失敗時に例外発生ではなく、null を返してくれるキャスト。
そのため、Type は null を代入できる参照型となるが、C# のみ、null 許容型も指定可能。

☆1 C# の (Type)value と VB の CType(value, Type) の結果が異なるパターン
 ※全パターン探ってないため、他にもあるかも…
浮動小数点→整数
変換例は★1
(Type)value 0 方向への丸め(切り捨て)。
C や Java のキャストと同じ。
Math.Truncate(value) でも同じ結果だが、型は変えてくれない。
CType(value, Type) 偶数方向へ丸める四捨五入(銀行丸め)。
Convert.ToInt32(value) と同じ。
Math.Round(value) でも同じ結果だが、型は変えてくれない。

★1 浮動小数点→整数の変換例
value(Type)valueCTypeConvert.ToInt32
-2.0-2-2-2
-1.6-1-2-2
-1.5-1-2-2
-1.4-1-1-1
-1.0-1-1-1
-0.60-1-1
-0.5000
-0.4000
0000
0.4000
0.5000
0.6011
1.0111
1.4111
1.5122
1.6122
2.0222

VB.NET の DirectCast、CType の使い分け

CType より DirectCast の方がパフォーマンスがいいため、DirectCast 可能な場合は、DirectCast を使う。
※ボックス・アンボックス化や、列挙型とその基になる型の相互変換など
String へ変換したい場合は ToString()、String から変換したい場合はそのクラス・構造体の Parse()、TryParse() が大抵使えるので CType は避けるべき。
浮動小数点→整数は、Math のメソッドで丸め後に CType が明示的で分かり易いかも。
整数→整数、整数→浮動小数点は、丸めを気にしなくていいので、素直に CType を使う。
Operator で独自実装している場合は、当然 CType を使用。

VB.NET で C# のキャスト (Double→Integer)

  • Math.Truncate を使う方法
※ Single や Decimal の場合は、引数の型を変えるだけでOK。

Public Shared Function CastToInt(ByVal val As Double) As Integer
    Return CType(Math.Truncate(val), Integer)
End Function

  • MSIL を使う方法
C# で作成した「double 引数を int でキャストして返す」メソッドの MSIL を基に作成。
C# のキャストと同じ結果・挙動になるが、この方法を採るくらいだったら、C# の DLL にキャストメソッドを実装して呼んだ方がまし…

Public Shared Function CastToIntIl(ByVal val As Double) As Integer
    Return DirectCast(_castToIntIlInternal.Value.Invoke(Nothing, New Object() {val}), Integer)
End Function
Private Shared _castToIntIlInternal As New Lazy(Of DynamicMethod)(
    Function()
        Dim instance As New DynamicMethod("CastToIntIlInternal", GetType(Integer), {GetType(Double)}, True)
        Dim il = instance.GetILGenerator()
        With il
            .Emit(OpCodes.Ldarg_0)
            .Emit(OpCodes.Conv_I4) 'キャスト処理(オーバーフローチェック無し)に相当
            .Emit(OpCodes.Ret)
        End With
        Return instance
    End Function
)

検証環境

Windows 7 64bit/Visual Studio 2010 SP1/.NET 4.0

0 件のコメント:

コメントを投稿