2.17. Conversion Operators

C# provides a mechanism by which each class can define a set of both implicit and explicit conversions that can be applied to objects of its class type. Why might we wish to do that?

Imagine that we have designed a BitVector class. As a convenience to our users, we choose to convert its internal sequence of 0,1 bits into either an unsigned long or a string representation. Similarly, we may wish to allow a string or unsigned long to be used wherever a BitVector is expected and have the program know how to convert those representations into a BitVector.

In one direction, we provide one or more conversion algorithms to change an object of the class type into a different type. Typically, these operators are specified as implicit because they should always succeed. This means that they are carried out automatically by the compiler, and therefore that they participate in overloaded-function resolution.

For example, the conversion of our BitVector into a string or ulong is well behaved. Here is how we might use it:

// our overloaded methods
public static void display( string s ){ ... }
public static void display( HashTable ht ){ ... }
public static void Main()
{
      BitVector bv = new BitVector( 32 );

      // implicitly convert bv into a string
      display( bv );

      // implicitly convert bv into a ulong
      ulong ul = bv;
}

Here is how we might declare the conversion operators to support these two implicit BitVector conversions:

public class BitVector
{
      static public implicit
             operator string( BitVector bv ){ ... }

      static public implicit
             operator ulong( BitVector bv ){ ... }

      // ... rest of BitVector class definition
}

Like an overloaded operator, a conversion operator must be both static and public. In addition, it must specify either implicit or explicit as a keyword. The type following the operator keyword is the return type of the conversion. The single parameter represents the source type to which the conversion should be applied. It cannot be either a ref or an out parameter. Either the return type or the parameter must be the type of the class to which the conversion operator belongs. In our example, both operators convert a BitVector object. In the first instance the operator turns the BitVector into a string type; in the second, into a ulong type.

In the other direction we provide one or more conversion algorithms to change an object of some other type into an object of the class type. We typically specify such conversions as explicit, meaning that to be carried out, the conversion requires an explicit cast by the user. There are two reasons for not making both conversion directions implicit.

The first reason is that if we specify both conversion directions as implicit, the compiler is likely to flag certain program situations as ambiguous. For example, imagine that we write the following:

classType + built-in-type;

The only two addition operators available are the addition operator taking two classType operands and the predefined addition operator taking two built-in-type operands. That is, for the addition shown here to be carried out, one or the other operand must be converted to the other. But which one?

If classType has defined two implicit conversion operators—one to convert the classType into the built-in-type and a second to convert the built-in-type into the classType-then the compiler is left with no clear choice, other than to flag the expression as ambiguous. By making one direction explicit, we make the ambiguity go away.

But how do we choose which conversion direction to make explicit? Experience has shown that the conversion of an object of some other type into an object of the class type is more likely to fail. For example, what should the conversion operator do if the string to be converted is null? The chance of the conversion failing is the second reason for choosing not to make a conversion implicit.

Here is how we might define our explicit pair of conversion operators:

static public explicit
       operator BitVec( string s )
{ ... }

static public explicit
       operator BitVec( ulong ul )

{ ... }

Here is how we might use them:

public static void display( BitVec bitvec ){ ... }
public static void display( HashTable ht ) { ... }

public static void Main()
{
   // both operators invoke display( BitVec );

   display( (BitVec) "0111001011010110" );
   display( (BitVec)43690 );
}

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

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