Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting Causes Different GetTypes

Tags:

c#

casting

So I don't have an in-depth knowledge of how data is stored in the .NET Framework in terms of custom types, but I was looking for an explanation on how the casting system is working.

For example, if one were to do an explicit cast from a ValueType Struct like Char into Byte as follows:

char C = '#';
byte B = (byte)C;
Console.WriteLine(B.GetType()); // Outputs "Byte"

I would be told that B is a Byte, which makes perfect sense.

Now say I cast from a Custom Class Flower to its base Class Plant, why does the output reveal the derived class regardless of the cast as follows:

class Plant{}
class Flower:Plant{}

.

Flower Rose = new Flower();
Plant RoseBush = (Plant)Rose;
Console.WriteLine(RoseBush.GetType()); //Outputs Flower

Plant Rose = new Flower();
Plant RoseBush = (Plant)Rose;
Console.WriteLine(RoseBush.GetType()); //Outputs Flower as well

I guess my question is, why doesn't the type expose the current type of the custom type, that being Plant, and why does this differ from the Value Types at the beginning?

Also, why is the two examples with the flower and plants, although written differently on the first line, output the same thing?

like image 626
Alessandro Farace Avatar asked Jul 13 '17 10:07

Alessandro Farace


2 Answers

The result of the first cast is a different value. It's now a byte not a char.

The second cast is a reference conversion. The result is the same set of bits - a reference to the same object - just with a different compile-time type. GetType() returns the actual execution-time type of the object, which is the same in both cases (because it's the same object).

You can observe this in your example:

// Variable names modified to follow normal conventions
Flower rose = new Flower();
Plant roseBush = (Plant) rose;
Console.WriteLine(ReferenceEquals(rose, roseBush)); // True

Casts for reference types can result in different values, mind you, if they're custom conversions - but those aren't "reference conversions" in language spec terminology. For example:

XElement element = new XElement("name", "value");
string value = (string) element;

Here value really isn't a reference to the same object as element - it's a reference to a string object instead of an XElement object, via the explicit conversion to string defined by XElement.

like image 165
Jon Skeet Avatar answered Oct 03 '22 22:10

Jon Skeet


The C# cast syntax does different things in different cases. When you talk about a class and base-class / sub-class / interface, it is a reference preserving cast - all you're actually doing is a type check (and even that is only in the case where the compiler doesn't know that it must work - it is omitted when casting a sub-type to the base-type, for example). In this case, the only thing that changes is the type that the compiler / JIT is thinking of.

However, there are also type conversions. Those can be inbuilt (as is the case for primitives such as byte / char), or they can be supplied as custom conversion operators (via the implicit or explicit operator keywords). In that case, it is up to the code what happens (it could be anything).

So: the difference here is that one is an inbuilt primitive conversion, and one is a reference-preserving type check.

like image 25
Marc Gravell Avatar answered Oct 03 '22 23:10

Marc Gravell