Worum es hier geht ist das sogenannte Boxing und Unboxing.
Beim Boxing wird die Variable eines Types (FtStudent) in einen anderen Typ (von dem er ableitet, hier: Student) (implizit) konvertiert und fortan kannst du mit diesem nur noch so arbeiten wie mit dem Basistyp (Student). Die Variable ist aber trotzdem noch vom ursprünglichen Typ (FtStudent).
FtStudent ft = new FtStudent();
Student boxed = ft;
// boxed hat nur die Methoden von Student.
Willst du nun wieder die Methoden von FtStudent benutzen musst du die Variable unboxen. Dies geschieht explizit mit einem Cast.
FtStudent unboxed = (FtStudent) boxed;
// unboxed enthält die Methoden des FtStudent.
Wenn eine Variable also geboxt wird, wird sie fortan als Basistyp behandelt und hat nur dessen Methoden (und Properties etc.), auch wenn sie in Wirklichkeit immer noch von ursprünglichen Typ ist. Der Compiler lässt allerdings nicht zu, dass du die Methoden des unterliegenden Typen aufrufst, dazu musst du die Variable wieder unboxen.
(Hoffe das macht Sinn - wenn nicht such mal nach 'Boxing and Unboxing').
Wozu man das braucht? Wenn du eine abstrakte Klasse (oder meist ein Interface) definierst, dass die Methodensignaturen hat, welche eine Klasse implementieren musst, kannst du die Methoden, wo die Klasse hergenommen wird, so schreiben, dass sie nur die abstrakte Klasse/Interface erwartet.
Dies bedeutet, dass, wenn du eine andere Implementierung haben willst, du nur noch an wenigen Stellen die Klasse ändern musst, und die Parameter der Methode gar nicht anfassen musst.
// Methode braucht Student
public void DoSomething(Student s)
{
// Do something.
}
// Im Moment ist deine Implementierung des Studenten FtStudent.
Student s = new FtStudent();
DoSomething(s);
//Wenn du jetzt die Implementierung ändern willst musst du nur ein Wort austauschen. So schaut dann der neue Code aus.
Student s = new PtStudent();
DoSomething(s);