Deep C# - Casting the Escape from Strong Typing
Written by Mike James   
Monday, 04 December 2023
Article Index
Deep C# - Casting the Escape from Strong Typing
Type Conversion
Downcast
Casting and Overriding

Casting and Overriding

You need to be aware that considerations of virtual versus non-virtual inheritance also come into play with both up- and downcasting. If a method is defined to be virtual then it is late bound. If a method isn’t virtual, or if the override uses the new modifier, then it is early bound.

For upcasting the way that this works has already been described in Chapter 5. If MyClassB inherits MyMethod from MyClassA and overrides it then:

ClassA myA = new MyClassB();
myA.MyMethod();

will call the method defined on MyClassB if it was declared virtual and the method defined on MyClassA if it wasn’t declared virtual or if new was used in the MyClassB override.

The same considerations apply to downcasting, but things are a little bit more complicated because, as already mentioned, a downcast usually involves an earlier upcast. The downcast is always early bound, but any upcast is late or early bound depending on the way the methods are declared. An example should make this clear.

Suppose we have three classes which form an inheritance chain and each of the methods is non-virtual and hence early bound:

class MyClassA
{
	public void MyMethod()
    	{
        	Console.WriteLine("A");
    	}
}
class MyClassB : MyClassA {
    	public new void MyMethod()
    	{
        	Console.WriteLine("B");
    	}
};
class MyClassC : MyClassB {
    	public new void MyMethod()
    	{
        	Console.WriteLine("C");
    	}
};

Now we can implement a downcast:

MyClassA myA = (MyClassA) new MyClassC();
((MyClassB) myA).MyMethod();

Notice that myA is in fact a MyClassC, but the downcast is to a MyClassB. However, as early binding is used, we see the MyClassB method print B. The declared type of myA is MyClassB when the method is called.

If you now make the MyClassA MyMethod virtual and that of MyClassB override you will see no change and the MyClassB method is called – early binding is still used.

Finally, if you also change the MyClassC implementation of MyMethod to override then it is the MyClassC MyMethod that is called – late binding is used for the final upcast and you see C printed.

It can be subtle, but it is logical.

Arrays of Value Types

The earlier example of a sorting method brings us to an interesting problem in casting that isn’t much discussed. This isn’t a theoretical point but a practical decision made to make the language more efficient. 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 you unthinkingly writing this and causing the inefficient boxing of many integers this is not allowed and 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 because it is a very time consuming process.

There seems to be no better 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 conversion from object[] to int[].

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

In chapter but not in this extract

  • Custom Casts
  • The Cast System
  • Variance and Casting

Postlude

When you realize how confused the use of casting is in C# and most modern languages you can’t help but be unhappy about it. If you want to put some order into your use of casting then use it only for passive type conversions and for any active type conversion that involves manipulation of the representation of a type. If your real purpose is to provide active type conversion for your classes, then see the TypeConverter class and the IConvertible interface in the C# documentation.

Keep in mind that generics provide a good way of avoiding the use of casts, but that there are times when casting provides a more powerful solution to the same problem.

Deep C#

 Buy Now From Amazon

DeepCsharp360

 Chapter List

  1. Why C#?

    I Strong Typing & Type Safety
  2. Strong Typing
       Extract 
    Why Strong Typing
  3. Value & Reference
  4.    Extract Value And Reference
  5. Structs & Classes
       Extract
    Structs & Classes 
  6. Inheritance
      
    Extract
    Inheritance
  7. Interfaces & Multiple Inheritance
      
    Extract Interface
  8. Controlling Inheritance
    II Casting & Generics
  9. Casting - The Escape From Strong Typing
      
    Extract Casting I ***NEW!
  10. Generics
  11. Advanced Generics
  12. Anonymous & Dynamic
    Typing
    III Functions
  13. Delegates
  14. Multicast Delegates
  15. Anonymous Methods, Lambdas & Closures
    IV Async
  16. Threading, Tasks & Locking
  17. The Invoke Pattern
  18. Async Await
  19. The Parallel For
    V Data - LINQ, XML & Regular Expressions
  20. The LINQ Principle
  21. XML
  22. LINQ To XML
  23. Regular Expressions
    VI Unsafe & Interop
  24. Interop
  25. COM
  26. Custom Attributes
  27. Bit Manipulation
  28. Advanced Structs
  29. Pointers 

Extra Material

 <ASIN:1871962714>

 <ASIN:B09FTLPTP9>

downcast

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

Banner


Apollo Adds REST APIs For GraphQL
29/10/2024

Apollo has added a simpler way to integrate REST APIs into a federated GraphQL environment. Available now in public preview, can be used to map REST API endpoints to their GraphQL schema using a decla [ ... ]



Extend NGINX With The New JavaScript Module
28/10/2024

Inject middleware functionality into NGINX with the expressive power of Javascript. NGINX JavaScript or NJS for short is a dynamic module under which you can use scripting for hooking into the NGINX e [ ... ]


More News

espbook

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Monday, 04 December 2023 )