Widening, Narrowingキーワードは型変換演算子CTypeのオーバーロードを定義するときに使用します。 WideningはInteger→Doubleのように型変換に際して損失が生じないような型変換(拡大変換)の定義に使用し、Narrowingは逆にDouble→Integerのように損失が生じる可能性のある場合(縮小変換)の定義に使用します。
次の例は、直行座標形式で表された複素数CartesianComplexと極座標形式で表された複素数PolarComplexの二つの構造体を定義し、それぞれ型変換演算子をオーバーロードして相互に型変換出来るようにしたものです。 余談ですが、VB8からは正式にXMLコメントが使えるようになったので、XMLコメントを入力しようとするとC#と同じようなテンプレートが自動的に作成されます。
Imports System ''' <summary> ''' 極座標形式で表された複素数 ''' </summary> Structure PolarComplex Private r As Double Private a As Double Public Sub New(ByVal r As Double, ByVal a As Double) Me.r = r Me.a = a End Sub ''' <summary> ''' 長さ ''' </summary> Public Property Radius() As Double Get Return r End Get Set(ByVal value As Double) r = value End Set End Property ''' <summary> ''' 偏角 ''' </summary> Public Property Argument() As Double Get Return a End Get Set(ByVal value As Double) a = value End Set End Property ''' <summary> ''' PolarComplexからCartesianComplexへの型変換 ''' </summary> Shared Widening Operator CType(ByVal p As PolarComplex) As CartesianComplex Return New CartesianComplex(p.r * Math.Cos(p.a), p.r * Math.Sin(p.a)) End Operator Public Overrides Function ToString() As String Return String.Format("{0:F3}*{{cos({1:F3})+isin({1:F3})}}", r, a) End Function End Structure ''' <summary> ''' 直行座標形式で表された複素数 ''' </summary> Structure CartesianComplex Private r As Double Private i As Double Public Sub New(ByVal r As Double, ByVal i As Double) Me.r = r Me.i = i End Sub ''' <summary> ''' 実部 ''' </summary> Public Property Real() As Double Get Return r End Get Set(ByVal value As Double) r = value End Set End Property ''' <summary> ''' 虚部 ''' </summary> Public Property Imaginary() As Double Get Return i End Get Set(ByVal value As Double) i = value End Set End Property ''' <summary> ''' CartesianComplexからPolarComplexへの型変換 ''' </summary> Shared Widening Operator CType(ByVal c As CartesianComplex) As PolarComplex Return New PolarComplex( (c.r ^ 2.0 + c.i ^ 2.0) ^ 0.5, Math.Atan2(c.r, c.i)) End Operator Public Overrides Function ToString() As String If 0.0 <= i Then Return String.Format("{0:F3} + i{1:F3}", r, i) Else Return String.Format("{0:F3} - i{1:F3}", r, -i) End If End Function End Structure Class TypeConversionSample Public Shared Sub Main() Dim p As New PolarComplex(2, Math.PI / 3) Dim c As CartesianComplex Console.WriteLine("Polar to cartesian.") c = p Console.WriteLine("c = {0}", c) Console.WriteLine("p = {0}", p) Console.WriteLine("Cartesian to polar.") c = New CartesianComplex(1.414, -1.414) p = c Console.WriteLine("c = {0}", c) Console.WriteLine("p = {0}", p) End Sub End Class''' 極座標形式で表された複素数 ''' Structure PolarComplex Private r As Double Private a As Double Public Sub New(ByVal r As Double, ByVal a As Double) Me.r = r Me.a = a End Sub '''''' 長さ ''' Public Property Radius() As Double Get Return r End Get Set(ByVal value As Double) r = value End Set End Property '''''' 偏角 ''' Public Property Argument() As Double Get Return a End Get Set(ByVal value As Double) a = value End Set End Property '''''' PolarComplexからCartesianComplexへの型変換 ''' Shared Widening Operator CType(ByVal p As PolarComplex) As CartesianComplex Return New CartesianComplex(p.r * Math.Cos(p.a), p.r * Math.Sin(p.a)) End Operator Public Overrides Function ToString() As String Return String.Format("{0:F3}*{{cos({1:F3})+isin({1:F3})}}", r, a) End Function End Structure '''''' 直行座標形式で表された複素数 ''' Structure CartesianComplex Private r As Double Private i As Double Public Sub New(ByVal r As Double, ByVal i As Double) Me.r = r Me.i = i End Sub '''''' 実部 ''' Public Property Real() As Double Get Return r End Get Set(ByVal value As Double) r = value End Set End Property '''''' 虚部 ''' Public Property Imaginary() As Double Get Return i End Get Set(ByVal value As Double) i = value End Set End Property '''''' CartesianComplexからPolarComplexへの型変換 ''' Shared Widening Operator CType(ByVal c As CartesianComplex) As PolarComplex Return New PolarComplex( (c.r ^ 2.0 + c.i ^ 2.0) ^ 0.5, Math.Atan2(c.r, c.i)) End Operator Public Overrides Function ToString() As String If 0.0 <= i Then Return String.Format("{0:F3} + i{1:F3}", r, i) Else Return String.Format("{0:F3} - i{1:F3}", r, -i) End If End Function End Structure Class TypeConversionSample Public Shared Sub Main() Dim p As New PolarComplex(2, Math.PI / 3) Dim c As CartesianComplex Console.WriteLine("Polar to cartesian.") c = p Console.WriteLine("c = {0}", c) Console.WriteLine("p = {0}", p) Console.WriteLine("Cartesian to polar.") c = New CartesianComplex(1.414, -1.414) p = c Console.WriteLine("c = {0}", c) Console.WriteLine("p = {0}", p) End Sub End Class]]>
Polar to cartesian.
c = 1.000 + i1.732
p = 2.000*{cos(1.047)+isin(1.047)}
Cartesian to polar.
c = 1.414 - i1.414
p = 2.000*{cos(2.356)+isin(2.356)}}}
ここで、今回はWideningを使って定義しました。 Windingは拡大変換が行われる型変換を定義する際に用いられ、変換に際してデータが消失することがないことを示します。 逆に、Narrowingは縮小変換が行われる型変換を定義する際に用いられ、変換に際してデータが消失する可能性があることを示します。
上記の例をNarrowingを使って定義した場合は、「c = p」は「c = CType(p, CartesianComplex)」のように明示的に型変換しなければなりません。 このことから、Wideningは暗黙的な型変換を許可するという意味で、C#でいうところのimplicitと似た役割を持つと考えることもできます。 また、Narrowingについては明示的な型変換を強制するという意味で、同じくexplicitと考えることができます。 C#における型変換演算子のオーバーロードは[[programming/netfx2/overview/typeconversion]]を参照してください
また、型変換の定義は必ずしもそれぞれの型の中で定義しなければならないものではありません。 次の例のように、「ComplexCartesian→PolarCartesian」と「PolarCartesian→ComplexCartesian」の両方の型変換を一つの型の中で定義することも出来ます。
#code(vb){{
Structure CartesianComplex
' 中略
Shared Widening Operator CType(ByVal c As CartesianComplex) As PolarComplex
Return New PolarComplex( (c.r ^ 2.0 + c.i ^ 2.0) ^ 0.5, Math.Atan2(c.r, c.i))
End Operator
Shared Widening Operator CType(ByVal p As PolarComplex) As CartesianComplex
Return New CartesianComplex(p.Radius * Math.Cos(p.Argument), p.Radius * Math.Sin(p.Argument))
End Operator
' 中略
End Structure