MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Diferența între suprascriere și ascundere a metodelor

Anterior, am explorat două moduri de a schimba funcționalitatea metodelor moștenite de la clasa de bază: ascunderea și suprascrierea. Care este diferența dintre aceste două abordări?

Suprascrierea

Să luăm un exemplu cu suprascrierea metodelor:

class Person
{
   public string Name { get; set; }
   public Person(string name)
   {
       Name = name;
   }
   public virtual void Print()
   {
       Console.WriteLine(Name);
   }
}
class Employee : Person
{
   public string Company { get; set; }
   public Employee(string name, string company)
       : base(name)
   {
       Company = company;
   }

   public override void Print()
   {
       Console.WriteLine($"{Name} lucrează la {Company}");
   }
}

Utilizăm clasele în program:

Person tom = new Employee("Tom", "Microsoft");
tom.Print();        // Tom lucrează la Microsoft

Când se apelează tom.Print(), se execută implementarea metodei Print din clasa Employee, chiar dacă variabila tom este de tipul Person.

Pentru a lucra cu metode virtuale, compilatorul generează o tabelă de metode virtuale (Virtual Method Table sau VMT). În această tabelă se înregistrează adresele metodelor virtuale. Fiecare clasă are propria sa tabelă.

Când se creează un obiect al clasei, compilatorul transmite în constructorul obiectului un cod special, care leagă obiectul de tabelul VMT aferent.

La apelul unei metode virtuale, din obiect se extrage adresa tabelului său VMT. Din VMT se extrage adresa metodei și acesteia i se transferă controlul. Deci, procesul de selecție a implementării metodei se face în timpul execuției programului.

Este important de menționat că, deoarece mediul de execuție trebuie inițial să obțină adresa metodei din tabelul VMT, acest lucru încetinește ușor execuția programului.

Ascunderea

Acum să luăm aceleași clase Person și Employee, dar în loc de suprascriere vom folosi ascunderea:

class Person
{
   public string Name { get; set; }
   public Person(string name)
   {
       Name = name;
   }

   public void Print()
   {
       Console.WriteLine(Name);
   }
}

class Employee : Person
{
   public string Company { get; set; }
   public Employee(string name, string company)
           : base(name)
   {
       Company = company;
   }
   public new void Print()
   {
       Console.WriteLine($"{Name} lucrează la {Company}");
   }
}

Și să vedem ce se întâmplă în următorul caz:

Person tom = new Employee("Tom", "Microsoft");
tom.Print();        // Tom

Variabila tom reprezintă tipul Person, dar păstrează o referință la un obiect Employee. Cu toate acestea, când se apelează metoda Print, se va executa versiunea metodei definită în clasa Person, nu în clasa Employee. De ce?

Clasa Employee nu suprascrie metoda Print moștenită de la clasa de bază, ci de fapt definește o nouă metodă. Prin urmare, când se apelează tom.Print(), se apelează metoda Print din clasa Person.

Diferența principală între cele două abordări este că suprascrierea permite ca metoda să fie selectată la runtime în funcție de tipul obiectului real, pe când ascunderea determină ce metodă va fi executată bazându-se pe tipul variabilei prin care se face apelul.

← Lecția anterioară Lecția următoare →