Conversia tipurilor
În capitolul anterior am discutat despre conversiile obiectelor de tipuri simple. Acum vom aborda tema conversiei obiectelor claselor. Să presupunem că avem următoarea ierarhie de clase:
class Person
{
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
public void Print()
{
Console.WriteLine($"Person {Name}");
}
}
class Employee : Person
{
public string Company { get; set; }
public Employee(string name, string company) : base(name)
{
Company = company;
}
}
class Client : Person
{
public string Bank { get; set; }
public Client(string name, string bank) : base(name)
{
Bank = bank;
}
}
În această ierarhie de clase putem urmări următoarea linie de moștenire: Object (toate clasele moștenesc implicit de la tipul Object) -> Person -> Employee|Client.

În această ierarhie, tipurile de bază sunt la partea superioară, iar tipurile derivate sunt la partea inferioară.
Conversii ascendente - Upcasting
Obiectele unui tip derivat (care se află în partea de jos a ierarhiei) în același timp reprezintă și tipul de bază. De exemplu, un obiect Employee este în același timp și un obiect al clasei Person. Acest lucru este natural, deoarece fiecare angajat (Employee) este o persoană (Person). Și putem scrie, de exemplu, în felul următor:
Employee employee = new Employee("Tom", "Microsoft");
Person person = employee; // conversia de la Employee la Person
Console.WriteLine(person.Name);
În acest caz, variabilei person, care reprezintă tipul Person, i se atribuie o referință la obiectul Employee. Dar pentru a păstra o referință la un obiect al unei clase într-o variabilă a unei alte clase, este necesar să efectuăm o conversie de tipuri - în acest caz, de la tipul Employee la tipul Person.
Și cum Employee moștenește de la clasa Person, se efectuează automat o conversie ascendentă implicită - conversia la un tip care se află în partea superioară a ierarhiei claselor, adică la clasa de bază.
În final, variabilele employee și person vor indica același obiect în memorie, dar variabilei person îi va fi accesibilă doar acea parte care reprezintă funcționalitatea tipului Person.

Conversiile ascendente similare se efectuează în mod similar:
Person bob = new Client("Bob", "ContosoBank"); // conversia de la Client la Person
Aici, variabila bob, care reprezintă tipul Person, păstrează o referință la obiectul Client, prin urmare se efectuează de asemenea o conversie ascendentă implicită de la clasa derivată Client la tipul de bază Person.
Conversia ascendentă implicită se va întâmpla și în următorul caz:
object person1 = new Employee("Tom", "Microsoft"); // de la Employee la object
object person2 = new Client("Bob", "ContosoBank"); // de la Client la object
object person3 = new Person("Sam"); // de la Person la object
Deoarece tipul object este baza pentru toate celelalte tipuri, conversia către acesta se va efectua automat.
Conversii descendente - Downcasting
Pe lângă conversiile ascendente de la derivat la bază există și conversii descendente sau downcasting - de la tipul de bază la cel derivat. De exemplu, în următorul cod, variabila person păstrează o referință la obiectul Employee:
Employee employee = new Employee("Tom", "Microsoft");
Person person = employee; // conversia de la Employee la Person
Și poate apărea întrebarea dacă se poate accesa funcționalitatea tipului Employee prin variabila tipului Person. Dar astfel de conversii nu se efectuează automat, deoarece nu fiecare persoană (obiect Person) este un angajat al unei companii (obiect Employee).
Și pentru o conversie descendentă este necesară aplicarea unei conversii explicite, indicând între paranteze tipul la care trebuie efectuată conversia:
Employee employee1 = new Employee("Tom", "Microsoft");
Person person = employee1; // conversia de la Employee la Person
//Employee employee2 = person; // nu se poate, este necesară o conversie explicită
Employee employee2 = (Employee)person; // conversia de la Person la Employee
Să examinăm unele exemple de conversii:
// Obiectul Employee reprezintă de asemenea tipul object
object obj = new Employee("Bill", "Microsoft");
// pentru a accesa capacitățile tipului Employee, convertim obiectul la tipul Employee
Employee employee = (Employee) obj;
// obiectul Client reprezintă de asemenea tipul Person
Person person = new Client("Sam", "ContosoBank");
// conversia de la tipul Person la Client
Client client = (Client)person;
În primul caz, variabilei obj i se atribuie o referință la obiectul Employee, deci putem converti obiectul obj la orice tip care se află în ierarhia de clase între tipul object și Employee.
Dacă avem nevoie să accesăm unele proprietăți sau metode specifice ale obiectului, nu este obligatoriu să atribuim obiectul convertit unei variabile:
// Obiectul Employee reprezintă de asemenea tipul object
object obj = new Employee("Bill", "Microsoft");
// conversia la tipul Person pentru a apela metoda Print
((Person)obj).Print();
// sau așa
// ((Employee)obj).Print();
// conversia la tipul Employee pentru a obține proprietatea Company
string company = ((Employee)obj).Company;
În același timp, trebuie să fim prudenți cu astfel de conversii. De exemplu, ce se va întâmpla în următorul caz:
// Obiectul Employee reprezintă de asemenea tipul object
object obj = new Employee("Bill", "Microsoft");
// conversia la tipul Client pentru a obține proprietatea Bank
string bank = ((Client)obj).Bank;
În acest caz vom primi o eroare, deoarece variabila obj păstrează o referință la obiectul Employee. Acest obiect este de asemenea un obiect al tipurilor object și Person, prin urmare putem să-l convertim la aceste tipuri. Dar nu putem să-l convertim la tipul Client.
Un alt exemplu:
Employee employee1 = new Person("Tom"); // Eroare!
Person person = new Person("Bob");
Employee employee2 = (Employee) person; // Eroare!
În acest caz încercăm să convertim un obiect de tip Person la tipul Employee, iar obiectul Person nu este un obiect Employee. În ultimul caz, Visual Studio nu va indica că în această linie există o eroare, și linia va fi compilată normal, totuși în timpul executării programului vom primi o eroare.
În acest lucru constă și perfidia conversiilor, deci în astfel de situații trebuie să fim precauți.
Există mai multe modalități de a evita astfel de erori de conversie.
Modalități de conversie
În primul rând, puteți folosi cuvântul cheie as. Cu ajutorul lui, programul încearcă să convertească expresia la un anumit tip, fără să arunce o excepție. În cazul unei conversii eșuate, expresia va conține valoarea null:
Person person = new Person("Tom");
Employee? employee = person as Employee;
if (employee == null)
{
Console.WriteLine("Conversia a eșuat");
}
else
{
Console.WriteLine(employee.Company);
}
Trebuie menționat că variabila employee aici este definită nu doar ca o variabilă Employee, ci mai exact ca Employee? - după numele tipului se pune un semn întrebător. Acest lucru indică faptul că variabila poate conține atât valoarea null, cât și valoarea Employee.
A doua metodă constă în verificarea admisibilității conversiei cu ajutorul cuvântului cheie is:
value is type
Dacă valoarea din stânga operatorului reprezintă tipul indicat în dreapta operatorului, atunci operatorul is returnează true, altfel returnează false.
În plus, operatorul is permite să convertească automat valoarea la tip, dacă aceasta reprezintă acel tip. De exemplu:
Person person = new Person("Tom");
if (person is Employee employee)
{
Console.WriteLine(employee.Company);
}
else
{
Console.WriteLine("Conversia nu este permisă");
}
Expresia if (person is Employee employee) verifică dacă variabila person este un obiect de tip Employee. Și dacă person este un obiect Employee, atunci automat convertește valoarea variabilei person în tipul Employee și valoarea convertită este salvată în variabila employee.
Apoi, în blocul if, putem folosi obiectul employee ca o valoare de tip Employee.
Totuși, dacă person nu este un obiect de tip Employee, cum este în acest caz, o astfel de verificare va returna valoarea false, și conversia nu va avea loc.
Operatorul is poate fi folosit și fără conversie, verificând doar conformitatea cu tipul:
Person person = new Person("Tom");
if (person is Employee)
{
Console.WriteLine("Reprezintă tipul Employee");
}
else
{
Console.WriteLine("NU este un obiect de tip Employee");
}