Widening, Narrowingキーワードは型変換演算子CTypeのオーバーロードを定義するときに使用します。 WideningはInteger→Doubleのように型変換に際して損失が生じないような型変換(拡大変換)の定義に使用し、Narrowingは逆にDouble→Integerのように損失が生じる可能性のある場合(縮小変換)の定義に使用します。

Sponsored Link

次の例は、直行座標形式で表された複素数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