Author | Message | Time |
---|---|---|
Grok | There are a couple questions here. In comments and after the code sample. [code] class TS { public int lossyVal = 0; } TS ts = new TS(); ts.lossyVal = 555; // we will attempt to lose this data via conversions Object tt = new object(); // implicit cast tt = ts; Console.WriteLine("ts is type {0}", ts.GetType().ToString()); Console.WriteLine("tt is type {0}", tt.GetType().ToString()); // at this point, both tt and ts are varibles of type TS, so ... ts = tt; // why does compile fail without explicit cast? // is it because compiler cannot figure out what happened on "tt = ts" line? // I can buy that, but what about .. // cast ts to object, attempt to lose the lossyVal tt = (object) ts; Console.WriteLine("ts is type {0}", ts.GetType().ToString()); Console.WriteLine("tt is type {0}", tt.GetType().ToString()); // why is tt not demoted to System.Object, and lose the extra data?? // tt should be a type that doesnt have a member called lossyVal, but .. Console.WriteLine("guess what .. ({0})", ((TS)tt).lossyVal); [/code] Why did lossyVal not get clobbered when converting to Object? | May 10, 2010, 5:03 AM |
Myndfyr | Casting along objects that have inheritance-supported casting is basically like calling dynamic_cast<target-type> in C++. An object in memory only has one type identity - when you cast between TS and object, you're just telling the compiler what operations your variable reference supports. Let me write equivalent code in C++ (traditional C++, not C++/CLI); assume there are similar library methods and classes. [code] class TS : public Object { public: int lossyVal = 0; } void main() { TS* ts = new TS; ts.lossyVal = 555; Object* tt = new Object; tt = ts; /* note that with this assignment, the memory pointed to by tt goes out of scope and is leaked, and tt and ts both point to the same memory. */ Console::WriteLine("ts is type {0}", ts->GetType()->ToString()); Console::WriteLine("tt is type {0}", tt->GetType()->ToString()); /* The lines above should produce the same output you saw */ ts = dynamic_cast<TS*>(tt); /* Note that dynamic_cast is required here because the compiler needs to adjust the vtables. That's why the C# compiler fails, too - it needs to ensure type safety. */ tt = ts; /* C++ and C# both support this assignment without a cast. tt still points to the same memory and object identity pointed to by ts - the compiler just sees it as an Object* */ Console::WriteLine("guess what .. ({0})", (dynamic_cast<TS*>(tt))->lossyVal); } [/code] C# isn't a dynamic or scripting language - you can't change the type identity of an object once you create it in memory. You only converted it to Object as far as the compiler sees, because you have two local references (analogous to C++ pointers) with different types. There is one caveat - like C++, C# allows you to overload casting operators. It defines two kinds of casts, implicit and explicit, which vary based on whether you need to specify the cast (an example of an implicit cast is in the LINQ-to-XML library, where all references to names use the XName type, which has an implicit cast operator from string to XName). You could "demote" the object like you outlined here, but you'd actually be creating a new object: [code] class TS { public int lossyVal = 0; public static explicit operator object(TS source) { return new object(); } } [/code] Because overloading of casting operators is not an obvious activity which can result in pretty grungy side-effects (mostly with regard to memory and fragmentation), it's not a common practice in .NET libraries. Overloading operators (like + or whatnot) is much more common, and even that is pretty uncommon. | June 5, 2010, 8:36 AM |