C'è una differenza tra newe virtual/ override.
Potete immaginare che una classe, quando istanziata, non è altro che una tabella di puntatori, che indica l'implementazione effettiva dei suoi metodi. L'immagine seguente dovrebbe visualizzarlo abbastanza bene:

Ora ci sono diversi modi, è possibile definire un metodo. Ognuno si comporta in modo diverso quando viene utilizzato con l'ereditarietà. Il modo standard funziona sempre come illustrato nell'immagine sopra. Se si desidera modificare questo comportamento, è possibile associare diverse parole chiave al metodo.
1. Classi astratte
Il primo è abstract. abstracti metodi indicano semplicemente il nulla:

Se la tua classe contiene membri astratti, deve anche essere contrassegnata come abstract , altrimenti il compilatore non compilerà la tua applicazione. Non è possibile creare istanze di abstractclassi, ma è possibile ereditare da esse e creare istanze delle classi ereditate e accedervi utilizzando la definizione della classe di base. Nel tuo esempio questo sembrerebbe:
public abstract class Person
{
public abstract void ShowInfo();
}
public class Teacher : Person
{
public override void ShowInfo()
{
Console.WriteLine("I am a teacher!");
}
}
public class Student : Person
{
public override void ShowInfo()
{
Console.WriteLine("I am a student!");
}
}
Se chiamato, il comportamento di ShowInfo varia in base all'implementazione:
Person person = new Teacher();
person.ShowInfo(); // Shows 'I am a teacher!'
person = new Student();
person.ShowInfo(); // Shows 'I am a student!'
Tutti e due, Student s e Teachers sono Persons, ma si comportano in modo diverso quando viene chiesto di richiedere informazioni su se stessi. Tuttavia, il modo per chiedere loro di richiedere le loro informazioni è lo stesso: utilizzando l' Personinterfaccia di classe.
Quindi cosa succede dietro le quinte, quando erediti Person? Durante l'implementazione ShowInfo, il puntatore non punta più verso il nulla , ora punta all'implementazione effettiva! Quando si crea Studentun'istanza, indicaStudent s ShowInfo:

2. Metodi virtuali
Il secondo modo è usare i virtualmetodi. Il comportamento è lo stesso, tranne per il fatto che stai fornendo un'implementazione predefinita opzionale nella tua classe base. Le classi con virtualmembri possono essere istanziate, tuttavia le classi ereditate possono fornire implementazioni diverse. Ecco come dovrebbe effettivamente funzionare il tuo codice:
public class Person
{
public virtual void ShowInfo()
{
Console.WriteLine("I am a person!");
}
}
public class Teacher : Person
{
public override void ShowInfo()
{
Console.WriteLine("I am a teacher!");
}
}
La differenza fondamentale è che il membro di base Person.ShowInfonon punta più verso il nulla . Questo è anche il motivo per cui è possibile creare istanze di Person(e quindi non è necessario contrassegnarlo come abstractpiù):

Dovresti notare che per ora non sembra diverso dalla prima immagine. Questo perché il virtualmetodo punta a un'implementazione " il modo standard ". Utilizzando virtual, si può dire Persons, che possono (non è necessario ) fornire un'implementazione diversa per ShowInfo. Se fornisci un'implementazione diversa (usando override), come ho fatto per quanto Teachersopra, l'immagine sembrerebbe la stessa di abstract. Immagina, non abbiamo fornito un'implementazione personalizzata per Students:
public class Student : Person
{
}
Il codice si chiamerebbe così:
Person person = new Teacher();
person.ShowInfo(); // Shows 'I am a teacher!'
person = new Student();
person.ShowInfo(); // Shows 'I am a person!'
E l'immagine per Studentsarebbe simile a questa:

3. La magica `nuova` parola chiave alias" Shadowing "
newè più un trucco intorno a questo. È possibile fornire metodi in classi generalizzate, che hanno gli stessi nomi dei metodi nella classe / interfaccia di base. Entrambi indicano la propria implementazione personalizzata:

L'implementazione è simile a quella fornita. Il comportamento differisce, in base al modo in cui accedi al metodo:
Teacher teacher = new Teacher();
Person person = (Person)teacher;
teacher.ShowInfo(); // Prints 'I am a teacher!'
person.ShowInfo(); // Prints 'I am a person!'
Questo comportamento può essere voluto, ma nel tuo caso è fuorviante.
Spero che questo renda le cose più chiare da capire per te!