C# will convert (for example) an int
to a long
implicitly but will
only 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
without
losing any information. 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 (such as
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
(15 == 15/1
).
Given a fraction, you might want to provide an explicit conversion back to an integer, understanding that some information 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,
to keep this example simple.
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 (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 their 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( " Assigning 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 staticexplicit
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 staticimplicit
operator Fraction(int theInt) { Console.WriteLine("Implicitly converting int to Fraction"); returnnew 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