Team LiB   Previous Section   Next Section

12.4 Conversion Operators

C# will convert (for example) an int to a long implicitly and will allow you to convert a long to an int explicitly. The conversion from int to long is implicit because you know that any int will fit into the memory representation of a long. The reverse operation, from long to int, must be explicit (using a cast) because it is possible to lose information in the conversion:

int myInt = 5;
long myLong;
myLong = myInt;        // implicit
myInt = (int) myLong;  // explicit

You want to be able to convert your Fraction objects to intrinsic types (e.g., int) and back. Given an int, you can support an implicit conversion to a fraction because any whole value is equal to that value over 1 (e.g., 15==15/1).

Given a fraction, you might want to provide an explicit conversion back to an integer, understanding that some value might be lost. Thus, you might convert 9/4 to the integer value 2 (truncating to the nearest whole number).

A more sophisticated Fraction class might not truncate, but rather round to the nearest whole number. This idea is left, as they say, as an exercise for the reader.

You use the keyword implicit when the conversion is guaranteed to succeed and no information will be lost; otherwise you use explicit. Implicit and explicit are actually operators, often called cast or casting operators because their job is to cast from one type to another (e.g., int to Fraction or Fraction to int).

Example 12-3 illustrates how you might implement implicit and explicit conversions; detailed analysis follows.

Example 12-3. Conversion operators
using System;

public class Fraction
{
    private int numerator;
    private int denominator;

    // create a fraction by passing in the numerator
    // and denominator
    public Fraction(int numerator, int denominator)
    {
        this.numerator=numerator;
        this.denominator=denominator;
    }

    // overload the constructor to create a
    // fraction from a whole number
    public Fraction(int wholeNumber)
    { 
        Console.WriteLine("In constructor taking a whole number"); 
        numerator = wholeNumber; 
        denominator = 1; 
    } 

    // convert ints to Fractions implicitly 
    public static implicit operator Fraction(int theInt) 
    { 
        Console.WriteLine("Implicitly converting int to Fraction"); 
        return new Fraction(theInt); 
    } 

    // convert Fractions to ints explicitly 
    public static explicit operator int(Fraction theFraction) 
    { 
        Console.WriteLine("Explicitly converting Fraction to int"); 
        return theFraction.numerator /  
            theFraction.denominator; 
    } 


    // overloaded operator + takes two fractions
    // and returns their sum
    public static Fraction operator+(Fraction lhs, Fraction rhs)
    {
        // like fractions (shared denominator) can be added
        // by adding thier numerators
        if (lhs.denominator == rhs.denominator)
        {
            return new Fraction(lhs.numerator+rhs.numerator,
                lhs.denominator);
        }

        // simplistic solution for unlike fractions            
        // 1/2 + 3/4 == (1*4) + (3*2) / (2*4) == 10/8
        // this method does not reduce.
        int firstProduct = lhs.numerator * rhs.denominator;
        int secondProduct = rhs.numerator * lhs.denominator;
        return new Fraction(
            firstProduct + secondProduct,       
            lhs.denominator * rhs.denominator
            );
    }

    // test whether two Fractions are equal
    public static bool operator==(Fraction lhs, Fraction rhs)
    {
        if (lhs.denominator == rhs.denominator && 
            lhs.numerator == rhs.numerator)
        {
            return true;
        }
        // code here to handle unlike fractions
        return false;
    }

    // delegates to operator ==
    public static bool operator !=(Fraction lhs, Fraction rhs)
    {
        bool equality = lhs==rhs;
        return !(equality);
    }

    // tests for same types, then delegates
    public override bool Equals(object o)
    {
        if (! (o is Fraction) )
        {
            return false;
        }
        return this == (Fraction) o;
    }

    // return a string representation of the fraction
    public override string ToString()
    {
        String s = numerator.ToString() + "/" +
            denominator.ToString();
        return s;
    }


}


public class Tester
{
    public void Run()
    {
        Fraction f1 = new Fraction(3,4);
        Fraction f2 = new Fraction(2,4);
        Fraction f3 = f1 + f2;

        Console.WriteLine("adding f3 + 5..."); 
        Fraction f4 = f3 + 5; 
        Console.WriteLine("f3 + 5 = f4: {0}", f4.ToString()); 

        Console.WriteLine("\nAssigning f4 to an int..."); 
        int truncated = (int) f4; 
        Console.WriteLine("When you truncate f4 you get {0}", 
            truncated); 
    }
    static void Main()
    {
        Tester t = new Tester();
        t.Run();
    }
}
 Output:
adding f3 + 5...
Implicitly converting int to Fraction
In constructor taking a whole number
f3 + 5 = f4: 25/4

Assigning f4 to an int...
Explicitly converting Fraction to int
When you truncate f4 you get 6

In Example 12-3, you add a second constructor that takes a whole number and creates a Fraction:

public Fraction(int wholeNumber)
{
    Console.WriteLine("In constructor taking a whole number");
    numerator = wholeNumber;
    denominator = 1;
}

Notice that in this and the following code samples you add WriteLine() statements to indicate when you've entered the method. This is an alternative to stepping through in a debugger. While using the debugger is usually more effective, this kind of output can help you trace the execution of your program for review at a later time.

You want to be able to convert Fractions to and from ints. To do so, create the conversion operators. As discussed previously, converting from a Fraction to an int requires truncating the value, and so must be explicit:

public static explicit operator int(Fraction theFraction)
{
    Console.WriteLine("Explicitly converting Fraction to int");
    return theFraction.numerator / 
        theFraction.denominator;
}

Note the use of the explicit keyword, indicating that this requires an explicit cast from a Fraction to an int. You see the cast in the Run() method:

int truncated = (int) f4;

The cast from an int to a Fraction, on the other hand, is perfectly safe, so it can be implicit:

Fraction f4 = f3 + 5;

Notice that there is no explicit cast (in parentheses). When you add the int to the Fraction, the int is implicitly cast to a Fraction. The implementation of this is to create a new Fraction object and to return it:

public static implicit operator Fraction(int theInt)
{
    Console.WriteLine("Implicitly converting int to Fraction");
    return new Fraction(theInt);
}

Calling the implicit cast operator causes the constructor to be invoked:

public Fraction(int wholeNumber)
{
    Console.WriteLine("In constructor taking a whole number");
    numerator = wholeNumber;
    denominator = 1;
}

You see this sequence of events represented in the output.

Implicitly converting int to Fraction
In constructor taking a whole number
    Team LiB   Previous Section   Next Section