// This program illustrates the rules of type-casting in C# using System; class AA { public virtual void f() { Console.WriteLine("AA.f"); } } class BB : AA { public override void f() { Console.WriteLine("BB.f"); } } class CC : AA { string z = "abc"; // inherits AA.f public void g() { Console.WriteLine("CC.g, "+z); } public static explicit operator BB(CC n) // define type cast from CC to BB { return new BB(); } } interface I { void whatever(); } public class castings { public static void Main() { AA p,q,r; p = new AA(); q = new BB(); r = new CC(); q.f(); // which f will be called? BB.f ((AA)q).f(); // which f will be called? STILL BB.f (think about this) // r.g(); // compiler error. why? ((CC)r).g(); // casting down - what does this do // 1. tell compiler to treat r as a CC object, which is possible // 2. a runtime, perform type check ((CC)q).g(); // compiler doesn't know what's wrong: runtime exception. CC n = new CC(); // ((BB)n).f(); // invalid cast (compiler error) without explicit operator def. // However, static casting checks are limited in all OOP langauges: BB x = (BB)((object)3); // compiles if casted to object first; I y = (I) r; // casting to any interface is allowed by compiler. y.whatever(); // also compiles // these cases will only cause runtime errors, not compiler errors // And one last weird thing you can do in C#: ask the compiler // to forget static type information altogether. ((dynamic)2).anythingIwant = "abc"; // this compiles }//main } /* Because type casting from superclass to subclass is sometimes needed in OOP, we can never completely guarantee type safety at compile time. But these languages will generally be strict about type casting at runtime. At compile time, it tries... Note that the kind of type casting we're examining here is different from: int x = (int)3.14; // or (double)3, which gives 3.0 This type of operation should really be a special function, for it computes a new value from one type to another (between a double precision float to a signed int). It would be better if we wrote int(3.14) instead (like in python and some other languages). It's not really type casting in the sense of interpreting a pointer to a double as a pointer to an array of 8 chars: here the data does not change. Nothing is acutally computed. Only the type of the data is interpreted differently. It is this notion of type casting that needs to be restricted in order to guarantee type safety (at compile time or runtime). */