Рефлексия методов

33

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

Прикладной интерфейс Reflection API весьма обширен и имеет ясную логическую структуру, поэтому, уяснив одну его часть, нетрудно понять и все остальное.

Чтобы проиллюстрировать базовый процесс рефлексии (и оценить пользу от System.Type), создадим новое консольное приложение. Это приложение будет отображать детали методов, свойств, полей и поддерживаемых интерфейсов (а также другие интересные элементы данных) для любого из типов, содержащихся как в самом приложении, так и в сборке mscorlib.dll (доступ к которой все приложения .NET получают автоматически). После создания соответствующего проекта первым делом необходимо импортировать пространство имен System.Reflection:

// Для выполнения рефлексии должно импортироваться это пространство имен
using System.Reflection;

Далее потребуется модифицировать класс Program, определив в нем ряд статических методов так, чтобы каждый из них принимал единственный параметр System.Type и возвращал void.

Имея в своем распоряжении объект класса Type, можно получить список методов, поддерживаемых отдельным типом данных, используя метод GetMethods(). Ниже приведена одна из форм, подходящих для этой цели:

Methodlnfo[] GetMethods()

Этот метод возвращает массив объектов класса MethodInfo, которые описывают методы, поддерживаемые вызывающим типом. Класс MethodInfo находится в пространстве имен System.Reflection.

Класс MethodInfo является производным от абстрактного класса MethodBase, который в свою очередь наследует от класса MemberInfo. Это дает возможность пользоваться всеми свойствами и методами, определенными в этих трех классах. Например, для получения имени метода служит свойство Name. Особый интерес вызывают два члена класса MethodInfo: ReturnType и GetParameters().

Возвращаемый тип метода находится в доступном только для чтения свойстве ReturnType, которое является объектом класса Type. Метод GetParameters() возвращает список параметров, связанных с анализируемым методом. Ниже приведена его общая форма:

ParameterInfo[] GetParameters();

Сведения о параметрах содержатся в объекте класса ParameterInfо. В классе ParameterInfо определено немало свойств и методов, описывающих параметры. Особое значение имеют два свойства: Name — представляет собой строку, содержащую имя параметра, a ParameterType — описывает тип параметра, который инкапсулирован в объекте класса Type.

Давайте рассмотрим пример:

using System;
using System.Reflection;

namespace Reflect
{
    // Тестовый класс, содержащий некоторые конструкции
    class MyTestClass
    {
        double d, f;

        public MyTestClass(double d, double f)
        {
            this.d = d;
            this.f = f;
        }

        public double Sum()
        {
            return d + f;
        }

        public void Info()
        {
            Console.WriteLine(@"d = {0}
f = {1}",d,f);
        }

        public void Set(int a, int b)
        {
            d = (double)a;
            f = (double)b;
        }

        public void Set(double a, double b)
        {
            d = a;
            f = b;
        }

        public override string ToString()
        {
            return "MyTestClass";
        }
    }

    // В данном классе определены методы использующие рефлексию
    class Reflect
    {
        // Данный метод выводит информацию о содержащихся в классе методах
        public static void MethodReflectInfo<T> (T obj) where T: class
        {
            Type t = typeof(T);
            // Получаем коллекцию методов
            MethodInfo[] MArr = t.GetMethods();
            Console.WriteLine("*** Список методов класса {0} ***\n",obj.ToString());

            // Вывести методы
            foreach (MethodInfo m in MArr)
            {
                Console.Write(" --> "+m.ReturnType.Name + " \t" + m.Name + "(");
                // Вывести параметры методов
                ParameterInfo[] p = m.GetParameters();
                for (int i = 0; i < p.Length; i++)
                {
                    Console.Write(p[i].ParameterType.Name + " " + p[i].Name);
                    if (i + 1 < p.Length) Console.Write(", ");
                }
                Console.Write(")\n");
            }
        }
    }

    class Program
    {
        static void Main()
        {
            MyTestClass mtc = new MyTestClass(12.0,3.5);
            Reflect.MethodReflectInfo<MyTestClass>(mtc);

            Console.ReadLine();
        }
    }
}
Пример рефлексии методов

Вторая форма метода GetMethods()

Существует вторая форма метода GetMethods(), позволяющая указывать различные флажки для отфильтровывания извлекаемых сведений о методах. Ниже приведена эта общая форма метода GetMethods():

Methodlnfo[] GetMethods(BindingFlags флажки)

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

DeclaredOnly

Извлекаются только те методы, которые определены в заданном классе. Унаследованные методы в извлекаемые сведения не включаются

Instance

Извлекаются методы экземпляра

Nonpublic

Извлекаются методы, не являющиеся открытыми

Public

Извлекаются открытые методы

Static

Извлекаются статические методы

Два или несколько флажков можно объединить с помощью логической операции ИЛИ. Но как минимум флажок Instance или Static следует указывать вместе с флажком Public или NonPublic. В противном случае не будут извлечены сведения ни об одном из методов.

Форма BindingFlags метода GetMethods() чаще всего применяется для получения списка методов, определенных в классе, без дополнительного извлечения наследуемых методов. Это особенно удобно в тех случаях, когда требуется исключить получение сведений о методах, определяемых в классе конкретного объекта. В качестве примера попробуем выполнить следующую замену в вызове метода GetMethods() из предыдущей программы:

MethodInfo[] MArr = t.GetMethods(BindingFlags.DeclaredOnly | 
                                                              BindingFlags.Instance | BindingFlags.Public);
Использование второй формы метода GetMethods()

Как видите, теперь выводятся только те методы, которые явно определены в классе MyTestClass.

Пройди тесты
Лучший чат для C# программистов