One of the best restaurants in NJ, Frog and The Peach offers corporate lunch, outdoor dining, wine tasting dinners, and more!
Considerations in Using Operator Overloading

There are several issues that you need to keep in mind when using operator overloading, in order to avoid unanticipated behavior.

Inheritance

If the operand(s) of an overloaded operator do not match what is defined explicitly by the operator's declaration, the standard overload resolution rules apply in determining which overload to use, as well as how the result is applied. Consider the following example in Code Block 11:

' Code Block 11
Sub Main()
   Dim Q As New B
   Dim P As B = -Q ' This will fail
End Sub
Class A
    Public Shared Operator -(value As A) As A
   .
.
.
    End Operator
End Class
Class B
    Inherits A
End Class

Since no unary negation operator is defined for class B, the unary negation operator for calls A (from which B derives) will be used as would be expected from a normal overloaded function. However, after that code is run, the assignment will return a value of type A, and then attempt to assign it to a reference to the derived type B, so the assignment will fail with an "Invalid cast" runtime error.

Narrowing and Widening

One important thing to note is that any overload of CType that you create must be defined as Narrowing or Widening. In general, Widening should be used when there is no possibility of error in the cast, whereas Narrowing should be used whenever there is a possibility of the cast failing.

Let's consider two structures—structure Position holds a two-degree/direction pairs (one for latitude, one for longitude), whereas structure GPSCoordinate contains not only a Position object, but an Elevation value as well. It's clear that GPSCoordinate can hold any information that Position can, but not the reverse. You'd lose the elevation information. Therefore, we can describe the relation from Position to GPSCoordinate as Widening, and that from GPSCoordinate to Position as Narrowing. When casting between the two, we can define these relationships in the overloaded operators so that they can be enforced correctly, as given in Code Block 12:

' Code Block 12
Option Strict On
Module Module1

    Sub Main()
        Dim pos As New Position(47.5, CompassPoint.North, _
                          180.2, CompassPoint.West)
        Dim gps As GPSCoordinate
        gps = pos ' This is OK -- widening conversion

        gps.m_Elevation = 5.5
        pos = gps ' Uh, oh -- narrowing conversion!  Error when option strict is on.

    End Sub

    Enum CompassPoint
        North = 0
        East = 0
        South = 1
        West = 1
    End Enum

    Structure DegreeLine
        Public Property Degrees() As Double
            Get
                Return m_Degrees
            End Get
            Set(ByVal value As Double)
                m_Degrees = value
            End Set
        End Property
        Public Property Direction() As CompassPoint
            Get
                Return m_Direction
            End Get
            Set(ByVal value As CompassPoint)
                m_Direction = value
            End Set
        End Property
        Private m_Degrees As Double
        Private m_Direction As CompassPoint
    End Structure

    Structure Position
        Public Sub New(ByVal lat As DegreeLine, _
               ByVal lon As DegreeLine)
            Latitude = lat
            Longitude = lon
        End Sub
        Public Sub New(ByVal latdeg As Double, _
                       ByVal latdir As CompassPoint, _
                       ByVal londeg As Double, _
                       ByVal londir As CompassPoint)
            m_Latitude.Degrees = latdeg
            m_Latitude.Direction = latdir
            m_Longitude.Degrees = londeg
            m_Longitude.Direction = londir
        End Sub
        Public Property Latitude() As DegreeLine
            Get
                Return m_Latitude
            End Get
            Set(ByVal value As DegreeLine)
                m_Latitude.Degrees = value.Degrees
                m_Latitude.Direction = value.Direction
            End Set
        End Property
        Public Property Longitude() As DegreeLine
            Get
                Return m_Longitude
            End Get
            Set(ByVal value As DegreeLine)
                m_Longitude.Degrees = value.Degrees
                m_Longitude.Direction = value.Direction
            End Set
        End Property
        Private m_Latitude As DegreeLine
        Private m_Longitude As DegreeLine
    End Structure

    Structure GPSCoordinate
        Public Sub New(ByVal positionValue As Position, _
                       ByVal elevationValue As Double)
            m_Elevation = elevationValue
            m_Position.Latitude = positionValue.Latitude
            m_Position.Longitude = positionValue.Longitude
        End Sub
        Public Overloads Shared _
              Narrowing Operator CType( _
             ByVal value As GPSCoordinate) As Position
            If value.m_Elevation <> 0.0 Then
                Throw New ArgumentException() _
                ' Elevation information would be lost!
            End If
            Return New Position(value.m_Position.Latitude, value.m_Position.Longitude)
        End Operator
        Public Overloads Shared _
              Widening Operator CType( _
             ByVal value As Position) As GPSCoordinate
            Return New GPSCoordinate(value, 0.0)
        End Operator
        Public m_Position As Position
        Public m_Elevation As Double
    End Structure

End Module

Casting from GPSCoordinate to Position will throw an exception at runtime if the elevation portion of the GPSCoordinate is non-zero. To make matters worse, when Option Strict is turned on (which is a recommended practice), the line of code that attempts the cast will be in error, due to the "Narrowing" qualifier in the relevant cast operator, unless CType() is explicitly used. Assigning a Position type to a GPSCoordinate type, on the other hand, will always work regardless of whether or not Option Strict is on, since the cast in that direction is defined as Widening. In this case, we just define the missing elevation to be zero.

Note that, in this case, we've defined the overloaded operators on the GPSCoordinate structure. We could just as easily have defined them on the Position structure. As long as Position was used in the overloaded operators either as a return value or an argument, this would be perfectly legal. However, the Position structure does not otherwise depend on the GPSCoordinate structure, whereas, even without the overloaded operators, GPSCoordinate depends on the Position structure. Therefore, it makes the most sense to have the operators defined in the GPSCoordinate structure. Otherwise, if we decided to move just the position structure to a library, we'd be in the awkward position of the Position code and the GPSCoordinate code having to reference each other, which is not a good coding practice and defeats the whole purpose of having a library.

Pair Operations

The following pairs of operators are linked. if you define one of them, the other must be defined as well:

Copyright © 2012 The Frog and the Peach. All Rights Reserved