"Live always in the best company when you read." - Sydney Smith

One of the new features in VS2005 is the ability to override the behavior of your favorite operator in relation to your custom object. huh? What does that mean exactly? Bit easier to show than explain.

For the sake of this example, I'm going to build a class to hold fractions (i.e. an enumerator and a denominator) and to allow mathematical and comparison operations on them. Before we design the basic class, it calls a couple of math functions that we'll need to create first. LCM is the Least Common Multiple and GCD is the Greatest Common Divisor; these are used when normalizing denominators and to simplify fractions like 3/6 into their simplest form (1/2):

Public Function LCM(ByVal a As Integer, ByVal b As Integer) As Integer
    If a = 0 AndAlso b = 0 Then Return 0
    Return (a * b) \ GCD(a, b)
End Function

Public Function GCD(ByVal a As Integer, ByVal b As Integer) As Integer Dim t As Integer Do Until b = 0 t = b b = a Mod b a = t Loop Return a End Function

On to the basic class:

Public Class Fraction

Private _Numerator As Integer Private _Denominator As Integer

Public Sub New(ByVal Numerator As Integer, ByVal Denominator As Integer) SetValue(Numerator, Denominator) End Sub Public Sub SetValue(ByVal Numerator As Integer, ByVal Denominator As Integer) If Denominator = 0 Then Throw New Exception("Division by zero; denominator can't be zero") _Numerator = Numerator _Denominator = Denominator

If _Denominator < 0 Then _Numerator = 0 - _Numerator _Denominator = 0 - _Denominator End If

Dim d As Integer = GCD(_Numerator, _Denominator) If d > 1 Then _Numerator \= d _Denominator \= d End If End Sub

Public Function ToDecimal() As Decimal Return CDec(_Numerator) / CDec(_Denominator) End Function Public Overrides Function ToString() As String Return Me._Numerator & "/" & Me._Denominator End Function End Class

Ideally, we'd like to be able to treat them like numbers. But the following example won't work, because it doesn't know how to handle the "+" or "-" operators in the context of the Fraction class:

Dim x As New Fraction(1, 2)
Dim y As New Fraction(1, 3)
Dim z1 As Fraction
Dim z2 As Fraction

z1 = x + y z2 = x - y

To override the "+" and "-" operators, we create a shared function in the class that looks like this:

Public Shared Operator +(ByVal x As Fraction, ByVal y As Fraction) As Fraction
    Dim l As Integer = LCM(x._Denominator, y._Denominator)
    Dim nx As Integer = x._Numerator * (l \ x._Denominator)
    Dim ny As Integer = y._Numerator * (l \ y._Denominator)
    Return New Fraction(nx + ny, l)
End Operator

Public Shared Operator -(ByVal x As Fraction, ByVal y As Fraction) As Fraction Dim l As Integer = LCM(x._Denominator, y._Denominator) Dim nx As Integer = x._Numerator * (l \ x._Denominator) Dim ny As Integer = y._Numerator * (l \ y._Denominator) Return New Fraction(nx - ny, l) End Operator

Now the sample above works.

We can also override the various comparison operators to allow comparisons between fractions. Note that some operators require the another; i.e., if you override the "=" operator, you must also override the "<>" operator. For these examples, I've implemented the iComparable(Of Fraction) interface and re-used the CompareTo function in my operator overloads:

Public Function CompareTo(ByVal other As Fraction) As Integer Implements System.IComparable(Of Fraction).CompareTo
    Dim g As Integer = GCD(Me._Denominator, other._Denominator)
    Dim n1 As Integer = Me._Numerator * (other._Denominator \ g)
    Dim n2 As Integer = other._Numerator * (Me._Denominator \ g)
    If n1 < n2 Then
        Return -1
    ElseIf n1 > n2 Then
        Return 1
    Else
        Return 0
    End If
End Function

Public Overrides Function Equals(ByVal obj As Object) As Boolean If obj Is Nothing OrElse (Not Me.GetType Is obj.GetType) Then Return False End If Return (Me.CompareTo(CType(obj, Fraction)) = 0) End Function

Public Shared Operator <>(ByVal x As Fraction, ByVal y As Fraction) As Boolean Return (x.CompareTo(y) <> 0) End Operator Public Shared Operator =(ByVal x As Fraction, ByVal y As Fraction) As Boolean Return (x.CompareTo(y) = 0) End Operator Public Shared Operator <(ByVal x As Fraction, ByVal y As Fraction) As Boolean Return (x.CompareTo(y) < 0) End Operator Public Shared Operator >(ByVal x As Fraction, ByVal y As Fraction) As Boolean Return (x.CompareTo(y) > 0) End Operator

Also notice the Equals function has been overridden. This allows certain builtin lists and collections to be able to make use of our comparison capabilities. We can now execute code like this:

Dim x As New Fraction(1, 2)
Dim y As New Fraction(1, 3)
If x < y Then
    MsgBox(x.ToString & " is less than " & y.ToString)
End If

Once the iComparable interface is defined, it's not necessary to also override the comparison operators, but it does allow the code that uses your object to create much more readable code. Which of these 2 would you prefer to read:

If x.CompareTo(y) < 0 Then

If x < y Then

Not all operators will make sense in the context of your custom object. The class I created above is a math-based class, so the "+" and "-" operators make sense. If your class is a container of various data elements, it may only make sense to override the comparison operators.

More Info

ScrewTurn Wiki version 2.0.33. Current Page Count: 23. Some of the icons created by FamFamFam.