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