Conversion Operators


We have looked at the object lifecycle and different ways of creating and destroying objects, specifically defining ways of implementing constructors and destructors. There is yet another type of object construction we have to review. This section deals with conversion operators between different kinds of reference type and the effect that this has on the constructor invocation.

There exists a conversion mechanism between value types that allows an int to be converted into a long type or a short into an int type, and so on. Conversion operators can be used to convert between value and reference types this is either implicit or explicit depending on the type of conversion necessary. Implicit conversions differ from explicit conversions in that they don't have to use parenthesis to specify a cast from one type to another. The conversion below is implicit in that we use assignment to change the type from Bus to BigVehicle without specifying a cast directly in code.

     Bus bus;     BigVehicle big = bus; 

The converse scenario is an explicit conversion where the cast between two types has to be explicitly defined. In this example we change from a BigVehicle struct type to a Bus class type (both the class and struct will be shown at the end of this section).

     BigVehicle big;     Bus bus = (Bus)big; 

For the purposes of clarity the following examples will illustrate the use of both implicit and explicit conversion operators, although we will be more interested in the explicit conversion operator since we can effectively define a new type of object construction using this mechanism. Conversion operators can also be used to convert between class and struct values and vice versa.

To illustrate the use of conversion operators, we'll return to our Teacher class and add a simple struct called BigVehicle to supplement this, adding conversion operators so that we can cast implicitly and explicitly between these two types.

There are some new keywords that need be used for user-defined conversion, implicit and explicit. These methods must be created as static and must have the operator keyword to specify that we have overridden the behavior of the language operator. The implicit operator will create a new BigVehicle struct and copy the entire field values from the Bus class instance to the struct; it will use the bus parameter argument to do this. The explicit operator will use the big parameter to create a new instance of the Bus class by invoking the Bus class constructor and copying the BigVehicle value arguments to the Bus fields through the constructor initialization. The following code can be found in explicit_conversion.cs, and the conversion operators can be seen at the end of this listing:

     using System;     public struct BigVehicle     {       public string NumberPlate;       public bool inService;       public int seats;     }     public class Bus     {       string numberPlate;       bool inService;       int seats;       public string NumberPlate       {         get { return numberPlate;}         set { numberPlate = value; }       }       public bool InService       {         get { return inService;}         set { inService = value; }       }       public int Seats       {         get { return seats;}         set { seats = value; }       }       public Bus(string numberPlate, int seats, bool inService)       {         this.numberPlate = numberPlate;         this.seats = seats;         this.inService = inService;       }       public static implicit operator BigVehicle(Bus bus)       {         BigVehicle big;         big.inService = bus.NumberPlate;         big.seats = bus.Seats;         big.inService = bus.inService;         return big;       }       public static explicit operator Bus(BigVehicle big)       {         return new Bus(big.NumberPlate, big.seats, big.inService);       }     } 

In order to convert between the Bus object and the BigVehicle struct value and invoke the implicit operator we can use the following code in a Main() method. The BigVehicle struct will be created in the code and populated with the property gets of the Bus object.

     Bus bus1 = new Bus("Eazy Rider", 20, true);     BigVehicle big1 = bus1; 

Similarly, we can invoke the explicit conversion operator by doing an explicit cast between BigVehicle and Bus. When the operator is invoked it will create a new instance of the Bus class and pass the BigVehicle value to the three parameter constructor (the values are extracted from the BigVehicle fields. Using the explicit operator in this way enables us to define the Bus constructor as private. This means that we can't explicitly create a new instance of the Bus but can provide a conversion interface so that the only way to create a new Bus object would be to convert a BigVehicle into a Bus (or any other type into a Bus that has a an explicit operator defined for it).

     Bus bus2;     BigVehicle big2;     big2.inService = true;     big2.NumberPlate = "Eazy Rider";     big2.seats = 20;     bus2 = (Bus)big2; 

The use of operators is fairly non-restrictive, which means that we can define a code block to convert from any value type to any reference type and vice versa. However we can only define one operator per type, as the compiler wouldn't be able to determine which operator we wanted to use for a particular conversion.

Being able to cast implicitly between structs and classes allows us to define value-type semantics for a reference type. One possibility would be if we wanted to define a Code type that takes the format of a letter followed by a number. We could use the following code to define such a type:

     using System;     public class Code     {       private string code = String.Empty;       public Code(string code)       {         if(code!=null && code.Length == 2            && char.IsLetter(code[0]) && char.IsNumber(code[1]))           this.code = code.ToUpper();         else           throw new ArgumentException("Not a valid code");       }       public static implicit operator Code(string address)       {         return new Code(address);       }       public static explicit operator string(Code code)       {         return code.code;       }       public override string ToString()       {         return (string)this;       }     } 

We could enter the code explicitly for this type (Code myCode = "C3";) and so if we used this type in a collection, for instance, we wouldn't have to perform any further validation to ensure that the codes were of the correct format, and by overriding the explicit string conversion operator and the ToString() method, it can behave exactly like a string when we want it to. Be wary of doing this, however, as if it behaves too much like a string, it may cause confusion to consumers of our code.

Conversion operators can also be used in a base class to convert between two types and similarly used in a derived class (as they are static they are not inherited, however, the derived class will not use the conversion operators of the base class). In the code below we can make the assumption that the BigBus class is derived from the Bus class but use a Bus type reference for it. Even though the BigBus class may have its own conversion operators it will use the Bus class conversion implicit operator.

     Bus bus = new BigBus(40);     BigVehicle i = bus; 




C# Class Design Handbook(c) Coding Effective Classes
C# Class Design Handbook: Coding Effective Classes
ISBN: 1590592573
EAN: 2147483647
Year: N/A
Pages: 90

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net