Casting – the escape from strong typing
Article Index
Casting – the escape from strong typing
Upcasting
Downcasting
Generics and arrays
Custom casts

Downcasting and generics

Before moving on it is worth knowing that downcasting can be avoided by the use of generics. For example suppose you want to implement a sort algorithm that will work on any type of object. You could write it using Object types which by downcasting be made to reference any object type. Of course when the sort method returns the collection or array you have to use an explicit downcast to access there methods or more likely to convert them to the actual type in use. As an alternative you could use a generic method to do the sorting and supply the actual type begin sorted at design time as a generic parameter.

Banner

For example, if we define a sort method that works with objects:

public object[] MySort(object[] array)
{
//do the sort
return array;
}

then if we declare an array of custom objects:

MyClass[] testdata=new MyClass[10];

then we can sort the array using:

MyClass[] resultdata = (MyClass[]) 
MySort((object[])testdata);

There is a upcast to object [ ] in the call and a downcast to MyClass [ ] in the return.

Notice that this really does mean that the method can sort any type we care to throw at it – assuming it knows how to order the type, i.e. to know when MyClass [ i ] is greater or smaller than MyClass [ j ].

You can usually provide an order relation to a class by giving it a Compare method. In addition you can make the sort method safer by writing it so that it only works with arrays of classes that implement a compare Interface using introspection. There are lots of examples of this sort to be found in the framework in facilities introduced before generics.

Generics allow you to avoid using casts for this sort of general algorithm implementation. Instead of casting to object you simply use a generic parameter:

public T[]MySort<T>(T[] array)
{
//do the sort
return array;
}

The <T> means you will supply the type of the array when you use the method. For example:

MyClass[]resultdata = MySort<MyClass>(
testdata);

Notice no casts are necessary and the types can be checked at design time rather than runtime. In this sense generics are safer than casting but being able to work with any type at run time by casting is sometimes more powerful.

 

Arrays of value types

The previous example brings us to an interesting problem in casting that isn’t much discussed. When it happens it can leave you puzzled for some time. You might think that the previous example of sorting objects would work with:

int[] testdata = new int[] { 1, 2, 3 };
int[] result =
MySort((object[])testdata);

after all there is nothing wrong with the cast and the system should box the integer value type automatically into a reference type that can be cast to object. However to avoid accidentally writing the inefficient boxing of integer this is not allowed. You will see the error message:

Cannot convert from type 
int[] to object[]

This leads some programmers to conclude that you can’t cast arrays. You can cast arrays as long as they have the same number of dimensions but you can’t cast value arrays to an array of reference types – simply to avoid the cost of boxing the value types.

There seems to be no simple way around this problem than to convert the value type array to an array of reference types by way of iteration – even if this fact is hidden by the way it is written. For example, you can use the Array static class and its Copy method which will perform a cast on each element during the copy:

int[] testdata = new int[] { 1, 2, 3 };
object[] obtestdata =
new object[testdata.Length];
Array.Copy(testdata,obtestdata,
testdata.Length);
object[] result = MySort(obtestdata);

Notice that the array sizes have to match and that you can’t cast the object [ ] back to int [ ] for the same reason, i.e. unboxing is inefficient.

You can also use the newer generic method that is part of the Array static object to write the whole thing without using intermediate arrays:

object[] result= MySort(
Array.ConvertAll<int,object>(
testdata,delegate(int i){
return(object)i;}));

but you still have to also do the converstion from object [ ] to int [ ].

No matter how you approach this problem it is messy that the syntax has to change so much for a method that works by upcasting to object when you move from reference to value variables. Part of the solution is provided by custom casts.

Covariance and Contravariance

You may not have noticed but the role of input and output parameters forces on them a particular type of behaviour when it comes to casting.

An input parameter is something that is going to be worked on by the method and in principle therefore it could be substituted by a super class - i.e.a "bigger" object - and the method should still work.This is because a super class is going to have all of the methods and properties that the method expects and perhaps a few more that it can just ignore.

This ability to accept a bigger object i.e. you can safely downcast the parameter,  is often referred to as contravariance.

Now if you consider a return parameter then the oposite situation applies - in this case it is being created so that something else can work on it and as such that somethng else can treat it as if it was a "lesser" object if it wants to.That is the code that accepts and uses the return parameter is free to ignore aspects of the return type and so it can treat the result as a sub class if it wants to.

That is the return parameter can be safely upcast to a smaller object and this in turn is refered to as covariance.

So in the current jargon input parameters are contravariant, output parameters are covariant and in/out parameters are invariant because they can't be safely cast in either direction at all.

Banner

You will find the terms covariance and contravariant , which come from category theory via physics, used in wider contexts to indicate the possibility of up or down casting in general. In particular it relates to new features in C# delegates and how there parameters can be implicitly cast a subject of a later chapter of Deep C#.

<ASIN:0735619573>

<ASIN:0672328917>

<ASIN:1430210842>



 
 

   
RSS feed of all content
I Programmer - full contents
Copyright © 2014 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.