Introducere în reflecție - Clasa System.Type
Reflecția reprezintă procesul de identificare a tipurilor în timpul rulării aplicației. Fiecare aplicație conține un set de clase utilizate, interfețe, precum și metodele, proprietățile și alte componente ale acestora. Reflecția permite determinarea tuturor acestor elemente componente ale aplicației. Principala sarcină a reflecției este studierea tipurilor.
Funcționalitatea principală a reflecției este concentrată în spațiul de nume System.Reflection. Aici, putem evidenția următoarele clase principale:
- Assembly: clasa care reprezintă o asamblare și permite manipularea acesteia
- AssemblyName: clasa care stochează informații despre asamblare
- MemberInfo: clasa abstractă de bază care definește funcționalitatea comună pentru clasele EventInfo, FieldInfo, MethodInfo și PropertyInfo
- EventInfo: clasa care stochează informații despre un eveniment
- FieldInfo: stochează informații despre un anumit câmp al unui tip
- MethodInfo: stochează informații despre o anumită metodă
- PropertyInfo: stochează informații despre o proprietate
- ConstructorInfo: clasa care reprezintă un constructor
- Module: clasa care permite accesul la un anumit modul dintr-o asamblare
- ParameterInfo: clasa care stochează informații despre un parametru al unei metode
Aceste clase reprezintă blocurile componente ale unui tip și aplicației: metode, proprietăți etc. Pentru a obține informații despre membrii unui tip, trebuie să utilizăm clasa System.Type.
Clasa Type reprezintă tipul studiat, încapsulând toate informațiile despre acesta. Cu ajutorul proprietăților și metodelor sale, putem obține aceste informații. Unele dintre proprietățile și metodele sale sunt:
- FindMembers() returnează un array de obiecte MemberInfo al tipului dat
- GetConstructors() returnează toți constructorii tipului dat sub formă de set de obiecte ConstructorInfo
- GetEvents() returnează toate evenimentele tipului dat sub formă de array de obiecte EventInfo
- GetFields() returnează toate câmpurile tipului dat sub formă de array de obiecte FieldInfo
- GetInterfaces() obține toate interfețele implementate de tipul dat sub formă de array de obiecte Type
- GetMembers() returnează toți membrii tipului sub formă de array de obiecte MemberInfo
- GetMethods() obține toate metodele tipului sub formă de array de obiecte MethodInfo
- GetProperties() obține toate proprietățile sub formă de array de obiecte PropertyInfo
- Name returnează numele tipului
- Assembly returnează numele asamblării în care este definit tipul
- Namespace returnează numele spațiului de nume în care este definit tipul
- IsArray returnează true dacă tipul este un array
- IsClass returnează true dacă tipul reprezintă o clasă
- IsEnum returnează true dacă tipul este o enumerație
- IsInterface returnează true dacă tipul reprezintă o interfață
Obținerea tipului
Pentru a manipula tipul și a obține toate informațiile despre acesta, trebuie mai întâi să obținem tipul respectiv. Acest lucru poate fi realizat în trei moduri: cu ajutorul operatorului typeof, folosind metoda GetType() a clasei Object și aplicând metoda statică Type.GetType().
Obținerea tipului prin typeof:
Type myType = typeof(Person);
Console.WriteLine(myType); // Person
public class Person
{
public string Name { get; }
public Person(string name) => Name = name;
}
Aici, este definită clasa Person cu o anumită funcționalitate. Pentru a obține tipul acesteia, se folosește expresia Type myType = typeof(Person);.
Obținerea tipului folosind metoda GetType, moștenită de la clasa Object:
Person tom = new Person("Tom");
Type myType = tom.GetType();
Spre deosebire de exemplul anterior, aici, pentru a obține tipul, trebuie să creăm un obiect al clasei.
Al treilea mod de obținere a tipului - metoda statică Type.GetType():
Type? myType = Type.GetType("Person", false, true);
Primul parametru indică numele complet al clasei cu spațiul de nume. Al doilea parametru indică dacă va fi generată o excepție în cazul în care clasa nu poate fi găsită. În acest caz, valoarea false înseamnă că excepția nu va fi generată.
Al treilea parametru indică dacă trebuie să se țină cont de sensibilitatea la majuscule și minuscule în primul parametru. Valoarea true înseamnă că sensibilitatea la majuscule și minuscule este ignorată. Deoarece tipul specificat poate lipsi, metoda returnează un obiect de tip nullable.
În acest caz, clasa principală a programului și clasa Person se află în spațiul de nume global. Totuși, dacă tipul este situat într-un alt spațiu de nume, acesta trebuie de asemenea specificat:
Type? myType = Type.GetType("PeopleTypes.Person", false, true);
Console.WriteLine(myType); // PeopleTypes.Person
namespace PeopleTypes
{
public class Person
{
public string Name { get; }
public Person(string name) => Name = name;
}
}
Ca alternativă, putem folosi operatorul typeof, transmițându-i numele tipului cu specificarea spațiului de nume:
Type myType = typeof(PeopleTypes.Person);
Dacă tipul necesar se află într-o altă asamblare dll, atunci după numele complet al clasei, se specifică numele asamblării, separat prin virgulă:
Type myType = Type.GetType("PeopleTypes.Person, MyLibrary", false, true);
Acum vom explora tipul și vom obține câteva informații despre el.
Type myType = typeof(PeopleTypes.Person);
Console.WriteLine($"Name: {myType.Name}"); // obținem numele scurt al tipului
Console.WriteLine($"Full Name: {myType.FullName}"); // obținem numele complet al tipului
Console.WriteLine($"Namespace: {myType.Namespace}"); // obținem spațiul de nume al tipului
Console.WriteLine($"Is struct: {myType.IsValueType}"); // verificăm dacă tipul este structură
Console.WriteLine($"Is class: {myType.IsClass}"); // verificăm dacă tipul este clasă
namespace PeopleTypes
{
class Person
{
public string Name { get; }
public Person(string name) => Name = name;
}
}
Output-ul consolei:
Name: Person
Full Name: PeopleTypes.Person
Namespace: PeopleTypes
Is struct: False
Is class: True
Căutarea interfețelor implementate
Pentru a obține toate interfețele implementate de tip, trebuie să utilizăm metoda GetInterfaces(), care returnează un array de obiecte Type:
Type myType = typeof(Person);
Console.WriteLine("Interfețele implementate:");
foreach (Type i in myType.GetInterfaces())
{
Console.WriteLine(i.Name);
}
public class Person : IEater, IMovable
{
public string Name { get; }
public Person(string name) => Name = name;
public void Eat() => Console.WriteLine($"{Name} mănâncă");
public void Move() => Console.WriteLine($"{Name} se mișcă");
}
interface IEater
{
void Eat();
}
interface IMovable
{
void Move();
}
Deoarece fiecare interfață reprezintă un obiect Type, pentru fiecare interfață obținută putem aplica metodele discutate mai sus pentru a extrage informații despre proprietăți și metode.
Însă până acum, toate exemplele de mai sus nu au folosit reflecția. În următoarele teme vom examina cum putem folosi reflecția pentru a obține componentele unui tip și pentru a le accesa, de exemplu, pentru a modifica valorile câmpurilor private ale unei clase.