If a class inherits from a base class you can always define a method that has a signature (reminder, that is the method head) that returns an object of the base class-type.
Since a derived class inherits all components from the base class, it can effectively be treated as the base class just as well. The runtime will always find an object, that has compatible properties, methods and so on.
So compilerwise it is perfectly fine and will never break to return an instance of a base class, even if you deliver a derived class in the implementation.
What I understood of the C# theory is that the result of this function
should be treated as an EasyMonster-type object, and there should be
no way for the program to keep in mind that sometimes the result of
the function MakingMonster() was, at the beginning, of the
DifficultMonster-type.
So this is not entirely correct. The syntax says, that whatever object is returned here will comply with the definition of a EasyMonster
-object. It does not need to be an EasyMonster
in itself, only that somewhere in the inheritance tree there is in fact the class EasyMonster
. The returned object however does retain all information it was constructed with. (Since it is a reference type, you only get a reference to the object anyways. So it is pointing to an instance of DifficultMonster
).
What I don't understand is why the EasyMonster part of the line
private static EasyMonster MakingMonster() doesn't force the type of
the variable returned to be EasyMonster. Why is it legal ?
Returning or casting an object to a base type does not strip away the "excess" part that was added during inheritence. It merely says that the object should now be treated as the base type for the runtime.
That also means, you have to be careful with overriding stuff that wasn't meant to be overridden in the first place. You can always override things (methods, properties, events etc.) with the new
keyword, but that can have unexpected results if you do not know how overriding works. For more detail, look here or here.
The is
keyword just checks if the object that your reference points to is compatible to the type description at runtime, see here. If you use the inheritance tree supplied by your classes, DifficultMonster
is both EasyMonster
and DifficultMonster
.
If you want to derive by your class structure that your classes are indeed not the same, than you have to introduce a base type from which both classes inherit like say Monster
:
class Monster
{
// implement all that monsters have in common.
}
and then you derive your EasyMonster
and your DifficultMonster
from your base class Monster
class EasyMonster : Monster
{
// implement specific things for the easy monster.
}
class DifficultMonster : Monster
{
// implement specific things for the difficult monster.
}
The method would then return an object of Type Monster
:
private static Monster MakingMonster()
{
if stuff
{
return new EasyMonster();
}
else
{
return new DifficultMonster();
}
}
And now the check will only be true if it is really a DifficultMonster
and not an EasyMonster
.
Monster monster = MakingMonster();
// Blah Blah
if (monster is DifficultMonster)
accDiff++; // only counts if the object is DifficultMonster
else
accEasy++; // only counts if the object is not a DifficultMonster
Since you probably don't want anyone to create an instance of your base class, because it doesn't make any sense having an undefined monster sitting around, you can then design the Monster
class to be abstract. Then only the derived classes can be instanced.
abstract class Monster { }
private static Monster MakingMonster()
{
return new EasyMonster(); // still works.
}
private static Monster MakingMonster()
{
return new Monster(); // will create compiler error.
}