The question is not silly at all; it is pretty deep and not trivial.
Very basically: the ability to It does know that because an executable image in the .NET assembly carries
metadata:
http://msdn.microsoft.com/en-us/library/ms404430%28v=vs.110%29.aspx[
^],
http://msdn.microsoft.com/en-us/library/xcd8txaw%28v=vs.110%29.aspx[
^].
In-depth MSDN article on
assignment compatibility:
http://msdn.microsoft.com/en-us/library/ee207183.aspx[
^].
Now, a non-trivial part of your situation is detecting of lack of assignment compatibility
during runtime. In many cases, the assignment-incompatible types can be detected statically, during compilation. In this case, metadata is also involved, but it can be used immediately by a compiler. A couple of samples:
class Vehicle {
}
class Person {
internal string SomeMember;
}
Vehicle v;
Person p = (Person) v;
I hope you understand why it cannot work. Class Vehicle does not have a member
SomeMember
, but
Person
does have it. When code could interpret the object
p
of the actual
runtime type Vehicle
as the object of the type
Person
, it would give the access to
non-existing member
Vehicle.SomeMember
. Note that even of you defined the same member in the clas
Vehicle
, it would be a different member with the same name, not the same as code>Person.SomeMember. The result of addressing to this non-existent member would be unpredictable.
The cases when such error cannot be detected at compilation are less trivial and are related to inheritance and the notions of
compile-time types and
runtime types. Let me show some example. I'll also introduce some dummy base type, to 1) show explicit inheritance, 2) to demonstrate two cases.
abstract class Person { }
enum LicenseClass { A, B, C, D, }
class Driver : Person {
internal LicenseClass { get; set; }
}
class Passenger: Person {
}
class Test {
internal static DoSomething(Person person) {
Driver driver = (Driver)person;
}
internal static DoSomethingWithDynamicCheck(Person person) {
Drive driver = person as Driver;
if (driver == null) return;
}
}
Driver someDriver = new Driver();
Person someOtherPerson = new Driver();
Test.DoSomething(someDriver);
Test.DoSomething(someOtherPerson);
Person someNonDriver = new Passenger();
Test.DoSomething(someNonDriver);
As you can see, statically, during runtime, the method
Test.DoSomethong
does not know if the runtime type is assignment-compatible with
Driver
or not; the compile-time type only says that it may me either
Driver
,
Passenger
or something else. This is validated only during runtime. How? My method
Test.DoSomethingWithDynamicCheck
shows the code behind this mechanism (approximately). Please see:
http://msdn.microsoft.com/en-us/library/cscsdfbt.aspx[
^],
see also:
http://msdn.microsoft.com/en-us/library/scekt9xw.aspx[
^].
But how the "
as
" operator can work? Because of the metadata embedded in the compiled assembly. You could also do it directly using Reflection and the type
System.Type
. This is one of the relevant reflection methods:
http://msdn.microsoft.com/en-us/library/vstudio/system.type.isassignablefrom[
^].
In a nutshell, the assembly metadata is the collection of objects representing all of the assembly types rather then instances of those types. Each instance of
System.Object
describes all the properties of the type, all its members and their respective properties.
—SA