Рефлексия (отражение) в C#. Введение

Рефлексия C# (или «отражение» в терминах Microsoft) — это, в первую очередь, процесс выявления типов (объектов типа Type) во время выполнения приложения. Любое наше приложение C# состоит из объектов, реализующих те или иные классы и интерфейсы, а также из методов, свойств объектов и других элементов. Рефлексия же (отражение) позволяет определить все эти элементы прямо во время выполнения приложение и, в случае необходимости, производить манипуляции с выявленными объектами.

Когда удобно использовать рефлексию (отражение в C#)

Рефлексию удобно использовать в следующих ситуациях:

  • При необходимости доступа к атрибутам в метаданных программы.
  • Для проверки и создания экземпляров типов в сборке.
  • Для создания типов во время выполнения.
  • Для выполнения позднего связывания, которое обеспечивает доступ к методам в типах, созданных во время выполнения.

Основные классы

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

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

Пример: получение информации о версии сборки в C# (класс AssemblyName)

Довольно часто в приложении необходимо выводить информацию о его версии, например, это может потребоваться при оформлении окна «About» вашего приложения. Для этого мы можем завести какую-либо константу, а можем использовать возможности, предоставляемые классом AssemblyName. Продемонстрируем это на примере:

namespace Reflection
{
    class Program
    {
        static void Main(string[] args)
        {
            //получаем информацию о сборке,
            //код которой выполняется в текущий момент времени
            Assembly assembly = Assembly.GetExecutingAssembly();
            //получаем отображаемое имя сборки
            Console.WriteLine(assembly.FullName);
            //Получаем подробную информацию об уникальном идентификаторе сборки
            AssemblyName assemblyName = assembly.GetName();
            //выводим простое имя сборки (без версии и т.д.)
            Console.WriteLine(assemblyName.Name);
            //выводим информацию о версии в виде одной строки
            Console.WriteLine($"Версия сборки: {assemblyName.Version}");
            //выводим детальную информацию о сборке
            Console.WriteLine($"Major {assemblyName.Version.Major}");
            Console.WriteLine($"Minor {assemblyName.Version.Minor}");
            Console.WriteLine($"Build {assemblyName.Version.Build}");
            Console.WriteLine($"MinorRevision {assemblyName.Version.MinorRevision}");
            Console.WriteLine($"MajorRevision {assemblyName.Version.MajorRevision}");
        }
    }
}

Результатом выполнения этого кода будет следующий вывод в консоли:

Reflection, Version=1.1.2.10, Culture=neutral, PublicKeyToken=null
Reflection
Версия сборки: 1.1.2.10
Major 1
Minor 1
Build 2
MinorRevision 10
MajorRevision 0

Кстати, используя класс Version в C#, достаточно просто сравнивать различные версии сборок, например, так:

Version currentVersion = assemblyName.Version;
Version newVersion = new Version("1.1.1.12");
Console.WriteLine($"Сравниваем текущую версию - {currentVersion} с новой - {newVersion}");
if (currentVersion == newVersion)
    Console.WriteLine("Версии равны");
else
    if (currentVersion < newVersion)
       Console.WriteLine("Новая версии больше текущей");
    else
       Console.WriteLine("Новая версии меньше текущей");

Класс System.Type

Чтобы получить информацию о членах типа, нам необходимо использовать класс System.Type. Этот класс представляет изучаемый тип, инкапсулируя всю информацию о нем. С помощью свойств и методов System.Type можно получить различную информацию об изучаемом классе. Вот только некоторые свойства и методы класса System.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, если тип представляет интерфейс

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

Чтобы управлять типом во время выполнения приложения  и получать всю информацию о нем, необходимо этот тип каким-либо способом получить. В C# это можно сделать тремя способами:

  1. с помощью ключевого слова typeof,
  2. с помощью метода GetType() класса Object
  3. применяя статический метод Type.GetType().

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

Определим в нашем приложении любой класс, например, вот такой:

class Person
    { 
        public string Name { get; set; }
        public string Family { get; set; }
        private byte age;
        public byte Age 
        {
            get 
            {
                return age;
            }
            set
            { if (value > 100)
                    throw new Exception("Возраст человека не может быть больше 100 лет");
                else
                    age = value;
                        
            }
        }
    }

Теперь воспользуемся ключевым словом typeof чтобы получить тип:

Type type = typeof(Person);
Console.WriteLine($"Type Name: {type.Name}");

Результат вывода в консоли:

Type Name: Person

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

Person user = new Person
{
    Name = "Вася",
    Family = "Пупкин",
    Age = 18
};

Type type = user.GetType();
Console.WriteLine($"Type Name: {type.Name}");

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

Использование статического метода Type.GetType()

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

Type type = Type.GetType("Reflection.Person", true, true);
Console.WriteLine($"Type Name: {type.Name}");

Здесь следует обратить внимание на то, что первый параметр указывает на полное имя класса, включая и пространство имен, в котором этот класс находится.  Второй параметр указывает, будет ли генерироваться исключение, если класс не удастся найти. В данном случае значение true означает, что исключение будет генерироваться. И третий параметр указывает, надо ли учитывать регистр символов в первом параметре. Значение true означает, что регистр игнорируется.

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

Type type = Type.GetType("Reflection.Person, MyLibrary", false, false);

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

Итого

Рефлексия C# — это процесс выявления типов (объектов типа Type) во время выполнения приложения (run-time). С помощью классов, содержащихся в пространстве имен System.Reflection мы можем получать информацию о сборке, используемых классах, свойствах классов и прочую информацию для использования в нашем же приложении. Так, например, используя класс AssemblyName можно довольно легко получить информацию о версии сборки и, при необходимости сравнить эту версию с другой.

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

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии