11.4. Operator Overloading

Both VB.NET and C# now support operator overloading, which means that you can define the behavior for standard operators such as +, −, /, and *. You can also define type conversion operators that control how casting is handled between different types.

11.4.1. Operators

The syntax for operator overloading is very similar to a static method except that it includes the Operator keyword, as shown in the following example:

C#

public class OperatorBaseClass{
    private int m_value;

    public static OperatorBaseClass operator +(OperatorBaseClass op1 ,
                                               OperatorBaseClass op2 )
    {
        OperatorBaseClass obc =new OperatorBaseClass();
        obc.m_value = op1.m_value + op2.m_value;
        return obc;
    }
}

VB.NET

Public Class OperatorBaseClass
    Private m_value As Integer

    Public Shared Operator +(ByVal op1 As OperatorBaseClass, _
                             ByVal op2 As OperatorBaseClass) As OperatorBaseClass
        Dim obc As New OperatorBaseClass
        obc.m_value = op1.m_value + op2.m_value
        Return obc
    End Operator
End Class

In both languages, a binary operator overload requires two parameters and a return value. The first value, op1, appears to the left of the operator, with the second on the right side. Clearly, the return value is substituted into the equation in place of all three input symbols. Although it makes more sense to make both input parameters and the return value the same type, this is not necessarily the case, and this syntax can be used to define the effect of the operator on any pair of types. The one condition is that one of the input parameters must be of the same type that contains the overloaded operator.

11.4.2. Type Conversions

A type conversion is the process of converting a value of one type to another type. These can be broadly categorized into widening and narrowing conversions. In a widening conversion, the original type has all the necessary information to produce the new type. As such, this conversion can be done implicitly and should never fail. An example would be casting a derived type to its base type. Conversely, in a narrowing conversion, the original type may not have all the necessary information to produce the new type. An example would be casting a base type to a derived type. This conversion cannot be guaranteed, and needs to be done via an explicit cast.

The following example illustrates conversions between two classes, Person and Employee. Converting from a Person to an Employee is a well-known conversion, because an employee's initial wage can be defined as a multiple of their age (for example, when they are employed). However, converting an Employee to a Person is not necessarily correct, because an employee's current wage may no longer be a reflection of the employee's age:

C#

public class Employee
{
    ...
    static public implicit operator Employee(Person p)
    {
        Employee emp=new Employee();
        emp.m_Name=p.Name;
        emp.m_Wage = p.Age * 1000;
        return emp;
    }
    static public explicit operator Person(Employee emp)
    {
        Person p = new Person();
        p.Name = emp.m_Name;
        p.Age=(int)emp.m_Wage/1000;
        return p;
    }
}

VB.NET

Public Class Employee
    ...
    Public Shared Widening Operator CType(ByVal p As Person) As Employee
        Dim emp As New Employee
        emp.m_Name = p.Name
        emp.m_Wage = p.Age * 1000
        Return emp
    End Operator
    Public Shared Narrowing Operator CType(ByVal emp As Employee) As Person
        Dim p As New Person
        p.Name = emp.m_Name
        p.Age = CInt(emp.m_Wage / 1000)
        Return p
    End Operator
End Class

11.4.3. Why Static Methods Are Bad

Now that you know how to overload operators and create your own type conversions, this section serves as a disclaimer stating that static methods should be avoided at all costs. Because both type conversions and operator overloads are static methods, they are only relevant for the type for which they are defined. This can cause all manner of grief and unexpected results when you have complex inheritance trees. To illustrate how you can get unexpected results, consider the following example:

Public Class FirstTier
    Public Value As Integer
    Public Shared Widening Operator CType(ByVal obj As FirstTier) As String
        Return "First Tier: " & obj.Value.ToString
    End Operator
    Public Overrides Function ToString() As String
        Return "First Tier: " & Me.Value.ToString
    End Function
End Class

Public Class SecondTier
    Inherits FirstTier
    Public Overloads Shared Widening Operator CType(ByVal obj As SecondTier) _
                                              As String
        Return "Second Tier: " & obj.Value.ToString
    End Operator

    Public Overrides Function ToString() As String
        Return "Second Tier: " & Me.Value.ToString
    End Function
End Class

'Sample code to call conversion and tostring functions
Public Class Sample
    Public Shared Sub RunSampleCode
        Dim foo As New SecondTier
        foo.Value = 5
        Dim bar As FirstTier = foo

        Console.WriteLine("<SecondTier> ToString " & vbTab & foo.ToString)
        Console.WriteLine("<SecondTier> CStr " & vbTab & CStr(foo))

        Console.WriteLine("<FirstTier> ToString " & vbTab & bar.ToString)
        Console.WriteLine("<FirstTier> CStr " & vbTab & CStr(bar))
   End Sub
End Class

The output from this sample is as follows:

<SecondTier> ToString   Second Tier: 5
<SecondTier> CStr       Second Tier: 5
<FirstTier> ToString    Second Tier: 5
<FirstTier> CStr        First Tier: 5

As you can see from the sample, the last cast gives an unusual response. In the first two casts, you are dealing with a SecondTier variable, so both ToString and CStr operations are called from the SecondTier class. When you cast the object to a FirstTier variable, the ToString operation is still routed to the SecondTier class, because this overrides the functionality in the FirstTier. However, because the CStr operation is a static function, it is routed to the FirstTier class, because this is the type of variable. Clearly, the safest option here is to ensure that you implement and call the ToString method on the instance variable. This rule holds for other operators, such as equals, which can be overridden instead of defining the = operator. In cases where you need a +, −, / or * operator, consider using nonstatic Add, Subtract, Divide, and Multiply operators that can be run on an instance.

As the final word on operator overloading and type conversion, if you find yourself needing to write either type of static method you should reassess your design to see if there is an alternative that uses instance methods such as Equals or ToString.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.13.192