Что такое рефлексия? Для решение каких задач приходилось использовать?

Рефлексия представляет собой процесс выявления типов во время выполнения приложения. Каждое приложение содержит набор используемых классов, интерфейсов, а также их методов, свойств и прочих кирпичиков, из которых складывается приложение. И рефлексия как раз и позволяет определить все эти составные элементы приложения. То есть основная задача рефлексии — это исследование типов.

Основной функционал рефлексии сосредоточен в пространстве имен System.Reflection. В нем мы можем выделить следующие основные классы:

  • Assembly: класс, представляющий сборку и позволяющий манипулировать этой сборкой
  • AssemblyName: класс, хранящий информацию о сборке
  • MemberInfo: базовый абстрактный класс, определяющий общий функционал для классов EventInfo, FieldInfo, MethodInfo и PropertyInfo
  • EventInfo: класс, хранящий информацию о событии
  • FieldInfo: хранит информацию об определенном поле типа
  • MethodInfo: хранит информацию об определенном методе
  • PropertyInfo: хранит информацию о свойстве
  • ConstructorInfo: класс, представляющий конструктор
  • Module: класс, позволяющий получить доступ к определенному модулю внутри сборки
  • ParameterInfo: класс, хранящий информацию о параметре метода

Эти классы представляют составные блоки типа и приложения: методы, свойства и т.д. Но чтобы получить информацию о членах типа, нам надо воспользоваться классом System.Type.

Класс Type представляет изучаемый тип, инкапсулируя всю информацию о нем. С помощью его свойств и методов можно получить эту информацию. Некоторые из его свойств и методов:

  • Метод FindMembers() возвращает массив объектов MemberInfo данного типа
  • Метод GetConstructors() возвращает все конструкторы данного типа в виде набора объектов ConstructorInfo
  • Метод GetEvents() возвращает все события данного типа в виде массива объектов EventInfo
  • Метод GetFields() возвращает все поля данного типа в виде массива объектов FieldInfo
  • Метод GetInterfaces() получает все реализуемые данным типом интерфейсы в виде массива объектов Type
  • Метод GetMembers() возвращает все члены типа в виде массива объектов MemberInfo
  • Метод GetMethods() получает все методы типа в виде массива объектов MethodInfo
  • Метод GetProperties() получает все свойства в виде массива объектов PropertyInfo
  • Свойство Name возвращает имя типа
  • Свойство Assembly возвращает название сборки, где определен тип
  • Свойство Namespace возвращает название пространства имен, где определен тип
  • Свойство IsArray возвращает true, если тип является массивом
  • Свойство IsClass возвращает true, если тип представляет класс
  • Свойство IsEnum возвращает true, если тип является перечислением
  • Свойство IsInterface возвращает true, если тип представляет интерфейс

Получение типа

Чтобы управлять типом и получать всю информацию о нем, нам надо сперва получить данный тип. Это можно сделать тремя способами: с помощью оператора typeof, с помощью метода GetType() класса Object и применяя статический метод Type.GetType().

Получение типа через typeof:

Type myType = typeof(Person);
 
Console.WriteLine(myType);  // Person
 
public class Person
{
    public string Name { get;}
    public Person(string name) => Name = name;
}

Здесь определен класс Person с некоторой функциональностью. И чтобы получить его тип, используется выражение Type myType = typeof(Person);

Получение типа с помощью метода GetType, унаследованного от класса Object:

Person tom = new Person("Tom");
Type myType = tom.GetType();

В отличие от предыдущего примера здесь, чтобы получить тип, надо создавать объект класса.

И третий способ получения типа — статический метод Type.GetType():

Type? myType = Type.GetType("Person", false, true);

Первый параметр указывает на полное имя класса с пространством имен. Второй параметр указывает, будет ли генерироваться исключение, если класс не удастся найти. В данном случае значение false означает, что исключение не будет генерироваться. И третий параметр указывает, надо ли учитывать регистр символов в первом параметре. Значение true означает, что регистр игнорируется. Поскольку указанный тип может отсутствовать, то метод возвращает объект nullable-типа

В данном случае класс основной программы и класс Person находятся в глобальном пространстве имен. Однако если тип располагается в другом пространстве имен, то его также надо указать:

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;
    }
}

В качестве альтернативы можно применять оператор typeof, передавая в него имя типа с указанием пространства имен:

Type myType = typeof(PeopleTypes.Person);

Если нужный нам тип находится в другой сборке dll, то после полного имени класса через запятую указывается имя сборки:

Type myType = Type.GetType("PeopleTypes.Person, MyLibrary", false, true);

Теперь исследуем тип и получим некоторую информацию о нем.

Type myType = typeof(PeopleTypes.Person);
 
Console.WriteLine($"Name: {myType.Name}");              // получаем краткое имя типа
Console.WriteLine($"Full Name: {myType.FullName}");     // получаем полное имя типа
Console.WriteLine($"Namespace: {myType.Namespace}");    // получаем пространство имен типа
Console.WriteLine($"Is struct: {myType.IsValueType}");  // является ли тип структурой
Console.WriteLine($"Is class: {myType.IsClass}");       // является ли тип классом
 
namespace PeopleTypes
{
    class Person
    {
        public string Name { get; }
        public Person(string name) => Name = name;
    }
}

Поиск реализованных интерфейсов

Чтобы получить все реализованные типом интерфейсы, надо использовать метод GetInterfaces(), который возвращает массив объектов Type:

Type myType = typeof(Person);
 
Console.WriteLine("Реализованные интерфейсы:");
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} eats");
 
    public void Move()=> Console.WriteLine($"{Name} moves");
}
interface IEater
{
    void Eat();
}
interface IMovable
{
    void Move();
}

Так как каждый интерфейс представляет объект Type, то для каждого полученного интерфейса можно также применить выше рассмотренные методы для извлечения информации о свойствах и методах

Но пока все примеры выше никак не использовали рефлексию. В следующих темах рассмотрим, как можно с помощью рефлексии получать компоненты типа и обращаться к ним, например, изменять значения приватных полей класса.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *