Quote:
What's the difference between compile-time types and runtime types? Moreover, what do they even mean?
Here's a quick example...
I have a class called "CharacterClass." Now, CharacterClass is an abstract class, meaning there are no such things as "CharacterClass" objects, however there can be objects of derived types.
Here's the class's declaration:
public abstract class CharacterClass { public abstract void Attack( Living thing); }
The above illustrates that I now have a CharacterClass
Type, however because it is abstract I cannot instantiate a new "CharacterClass" object. Also above we see there is a method of the class called Attack.
Now, lets make this more interested by providing some concrete classes which are based on CharacterClass.
public class Healer : CharacterClass { public override void Attack(Living thing) { UseWand(thing ); } protected void UseWand(Living thing) { } } public class Nuker : CharacterClass { public override void Attack(Living thing) { CastSpell(thing); } protected void CastSpell(Living thing) { } } public class Tank : CharacterClass { public override void Attack(Living thing) { // Bash ShieldBash(thing); } protected void ShieldBash(Living thing) { } }
Now you can see that I've got 3 concrete (not abstract) classes which inherit the Attack method from CharacterClass. Now, you'll notice I put override on each of those within the derived class. I did that because by making the Attack Method abstract in the base class, I implicitly made it virtual as well. In the derived classes I want to override the default implementation (which in this case didnt exist, as it was abstract and not just virtual).
Now I can do something like this in my program...
static void Main(string[] args) { // ... do lots of setup CharacterClass myClass = GetClassFromPlayer(); // .. do some more stuff myClass.Attack(someLiving); }
Ok, so that's an overly simplified example, but here's what's happening. Inside of my main fuction I'm declaring an object called myClass, but I'm saying it's of type CharacterClass. "OMGWTF!! You said I couldn't do that!!" Yeah, you're right, sortof.
In the example above I'm not actually instantiating an object of type CharacterClass, instead I'm an object of whatever type is returned from GetClassFromPlayer. Lets say for a moment that GetClassFromPlayer displays a menu for me. And then asks me which class I'd like to play. Assume for a moment that I select "Healer". What GetClassFromPlayer() will return is an object of type "Healer". However, as you'll learn more of when we get into the chapter on inheritance, since "Healer" inherits all the members of CharacterClass than a Healer IS-A Character Class.
In spite of the fact that I've created an object of type Healer, I'm allowed to assign it to a variable of type CharacterClass. Now, here's the magic. When I call "Attack()" on myClass, which Attack method does it call? CharacterClass's? Nope. It's Attack method was abstract, and so had no implementation. (In hindsight, it might have been better just to make CharacterClass's Attack method empty, rather than abstract so I could prove a point, oh well).
In reality, it will call the Attack() method owned by Healer. Why? Polymorphism. Because the Attack method in CharacterClass was abstract (and thus virtual) the application knows to call the Method associated with the object that was assigned to variable myClass, and not necessarily what type it has in front of it.
Ready for the punchline?
CharacterClass is the Compile-Time class associated with myClass.
Healer is the Run-Time class associated with myClass. Why is it "run-time"? Because it wasn't until run-time that the CLR program was able to determine what type of object would actually be stored in myClass.
Poof! There it is! Compile-Time types are the types as determined at compile-time. Run-time types are the types of objects ACTUALLY stored in a variable at run-time.
Cheers!