Исследование методов и конструкторов с помощью рефлексии

Последнее обновление: 17.02.2022

Получение информации о методах

Для получения получении информации отдельно о методах применяется метод GetMethods(). Этот метод возвращает все методы типа в виде массива объектов MethodInfo. Его свойства предоставляют информацию о методе. Отметим некоторые из его свойств:

  • IsAbstract: возвращает true, если метод абстрактный

  • IsFamily: возвращает true, если метод имеет модификатор доступа protected

  • IsFamilyAndAssembly: возвращает true, если метод имеет модификатор доступа private protected

  • IsFamilyOrAssembly: возвращает true, если метод имеет модификатор доступа protected internal

  • IsAssembly: возвращает true, если метод имеет модификатор доступа internal

  • IsPrivate: возвращает true, если метод имеет модификатор доступа private

  • IsPublic: возвращает true, если метод имеет модификатор доступа public

  • IsConstructor: возвращает true, если метод предоставляет конструктор

  • IsStatic: возвращает true, если метод статический

  • IsVirtual: возвращает true, если метод виртуальный

  • ReturnType: возвращает тип возвращаемого значения

Некоторые из методов MethodInfo:

  • GetMethodBody(): возвращает тело метода в виде объекта MethodBody

  • GetParameters(): возвращает массив параметров, где каждый параметр представлен объектом типа ParameterInfo

  • Invoke(): вызывает метод

Применим ряд свойств для исследования методов класса:

using System.Reflection;

Type myType = typeof(Printer);

Console.WriteLine("Методы:");
foreach (MethodInfo method in myType.GetMethods())
{
    string modificator = "";

    // если метод статический
    if (method.IsStatic) modificator += "static ";
    // если метод виртуальный
    if (method.IsVirtual) modificator += "virtual ";

    Console.WriteLine($"{modificator}{method.ReturnType.Name} {method.Name} ()");
}
class Printer
{
    public string DefaultMessage { get; set; } = "Hello";
    public void PrintMessage(string message, int times = 1)
    {
        while (times-- > 0) Console.WriteLine(message);
    }
    public string CreateMessage() => DefaultMessage;
}

На выходе получим следующую информацию:

Методы:
String get_DefaultMessage ()
Void set_DefaultMessage ()
Void PrintMessage ()
String CreateMessage ()
Type GetType ()
virtual String ToString ()
virtual Boolean Equals ()
virtual Int32 GetHashCode ()

Как видно из вывода в категорию методов также попадают и свойства, которые по сути представляют два метода: get и set. Если подобная ситуация не устраивает, то можно дополнительно фильтровать список методов:

foreach (MethodInfo method in myType.GetMethods()
            .Where(m => !m.Name.StartsWith("get_") && !m.Name.StartsWith("set_")))
{
 // .........
}

BindingFlags

В примере выше использовалась простая форма метода GetMethods(), которая извлекает все общедоступные публичные методы. Но мы можем использовать и другую форму метода: MethodInfo[] GetMethods(BindingFlags). Объединяя значения BindingFlags можно комбинировать вывод. Например, получим только методы самого класса без унаследованных, как публичные, так и все остальные:

using System.Reflection;

Type myType = typeof(Printer);

Console.WriteLine("Методы:");
foreach (MethodInfo method in myType.GetMethods(BindingFlags.DeclaredOnly
            | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
{
    Console.WriteLine($"{method.ReturnType.Name} {method.Name} ()");
}
class Printer
{
    public string DefaultMessage { get; set; } = "Hello";
    protected internal void PrintMessage(string message, int times = 1)
    {
        while (times-- > 0) Console.WriteLine(message);
    }
    private string CreateMessage() => DefaultMessage;
}

Теперь метод Print в классе Person является приватным, а метод SayMessage имеет модификатор protected internal.

Для получения всех непубличных методов в метод GetMethods() передается набор флагов BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, то есть получаем все методы экземпляра, как публичные, так и непубличные, но исключаем статические. Соответственно теперь получим следующий вывод:

Методы:
String get_DefaultMessage ()
Void set_DefaultMessage ()
Void PrintMessage ()
String CreateMessage ()

Исследование параметров

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

  • Attributes: возвращает атрибуты параметра

  • DefaultValue: возвращает значение параметра по умолчанию

  • HasDefaultValue: возвращает true, если параметр имеет значение по умолчанию

  • IsIn: возвращает true, если параметр имеет модификатор in

  • IsOptional: возвращает true, если параметр является необязательным

  • IsOut: возвращает true, если параметр является выходным, то есть имеет модификатор out

  • Name: возвращает имя параметра

  • ParameterType: возвращает тип параметра

Используем тип ParameterInfo для исследования параметров:

using System.Reflection;

foreach (MethodInfo method in typeof(Printer).GetMethods())
{
    Console.Write($"{method.ReturnType.Name} {method.Name} (");
    //получаем все параметры
    ParameterInfo[] parameters = method.GetParameters();
    for (int i = 0; i < parameters.Length; i++)
    {
        var param = parameters[i];
        // получаем модификаторы параметра
         string modificator = "";
        if (param.IsIn) modificator = "in";
        else if (param.IsOut) modificator = "out";

        Console.Write($"{param.ParameterType.Name} {modificator} {param.Name}");
        // если параметр имеет значение по умолчанию
        if (param.HasDefaultValue) Console.Write($"={param.DefaultValue}");
        // если не последний параметр, добавляем запятую
        if (i < parameters.Length - 1) Console.Write(", ");
    }
    Console.WriteLine(")");
}

class Printer
{
    public void PrintMessage(string message, int times = 1)
    {
        while (times-- > 0) Console.WriteLine(message);
    }
     public void CreateMessage(out string message) => message = "Hello Metanit.com";
}

Консольный вывод:

Void PrintMessage (String  message, Int32  times=1)
Void CreateMessage (String& out message)
Type GetType ()
String ToString ()
Boolean Equals (Object  obj)
Int32 GetHashCode ()

Стоит отметить, что если параметр имеет модификатор ref, in, out, то в конце названия типа добавляется амперсанд - String&.

Вызов методов

С помощью метода Invoke() можно вызвать метод:

public object? Invoke (object? obj, object?[]? parameters);

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

Вызов метода:

using System.Reflection;

var myPrinter = new Printer("Hello");

// получаем метод Print
var print = typeof(Printer).GetMethod("Print");
// вызываем метод Print
print?.Invoke(myPrinter, parameters: null); // Hello

class Printer
{
    public string Text { get;}
    public Printer(string text) => Text = text;
    public void Print() => Console.WriteLine(Text);
}

Метод GetMethod() возвращает метод, который имеет определенное имя - в данном случае метод Print. Далее используя полученный метод, его можно вызвать. Здесь при вызове в качестве первого параметра передается объект, для которого вызывается метод Print - объект myPrinter. И поскольку метод Print не принимает параметров, параметру parameters передается значение null.

Если метод непубличный, то для получения метода мы можем передать флаги в вызов GetMethod:

using System.Reflection;

var myPrinter = new Printer("Hello METANIT.COM");

// получаем метод Print
var print = typeof(Printer).GetMethod("Print",
            BindingFlags.Instance |
            BindingFlags.Public |
            BindingFlags.NonPublic);
// вызываем метод Print
print?.Invoke(myPrinter, parameters: null); // Hello METANIT.COM

class Printer
{
    public string Text { get;}
    public Printer(string text) => Text = text;
    private void Print() => Console.WriteLine(Text);
}

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

using System.Reflection;

var myPrinter = new Printer();
// получаем метод CreateMessage
var createMessage = typeof(Printer).GetMethod("CreateMessage");
// вызываем метод CreateMessage и получаем его результат
var result = createMessage?.Invoke(myPrinter, parameters: null);
Console.WriteLine(result);  // Hello Metanit.com

class Printer
{
    public string CreateMessage() => "Hello Metanit.com";
}

Стоит отметить, что результат метода представляет тип object?, соответственно при необходимости может потребоваться выполнить приведение типов.

Передача параметров:

using System.Reflection;

var myPrinter = new Printer();
// получаем метод PrintMessage
var printMessage = typeof(Printer).GetMethod("PrintMessage");
// вызываем метод PrintMessage, передавая ему два аргумента
printMessage?.Invoke(myPrinter, new object[] {"Hi world", 3});

class Printer
{
    public void PrintMessage(string message, int times)
    {
        while (times-- > 0) Console.WriteLine(message);
    }
}

Здесь метод PrintMessage имеет два параметра - messsage (некоторое соощение) и times (сколько раз надо вывести сообщение на консоль). И для этих параметров передаем массив аргументов new object[] {"Hi world", 3}. Таким образом, метод три раза выведет строку "Hi world".

Вызов обобщенного метода:

using System.Reflection;

var myPrinter = new Printer();
// получаем метод PrintValue
var printValue = typeof(Printer).GetMethod("PrintValue");
// получаем обобщенную версию метода для типа string
var printStringValue = printValue?.MakeGenericMethod(typeof(string));
// вызываем метод PrintValue, передавая ему строку
printStringValue?.Invoke(myPrinter, new object[] {"Hello world"});

class Printer
{
    public void PrintValue<T>(T value)
    {
        Console.WriteLine(value);
    }
}

Для получения обобщенной версии метода, которая типизирована определенным типом, у объекта MethodInfo вызывается метод MakeGenericMethod - в него передает тип, которым типизируется метод.

Получение конструкторов

Для получения конструкторов применяется метод GetConstructors(), который возвращает массив объектов класса ConstructorInfo. Этот класс во многом похож на MethodInfo и имеет ряд общей функциональности. Некоторые основные свойства и методы:

  • Свойство IsFamily: возвращает true, если конструктор имеет модификатор доступа protected

  • Свойство IsFamilyAndAssembly: возвращает true, если конструктор имеет модификатор доступа private protected

  • Свойство IsFamilyOrAssembly: возвращает true, если конструктор имеет модификатор доступа protected internal

  • Свойство IsAssembly: возвращает true, если конструктор имеет модификатор доступа internal

  • Свойство IsPrivate: возвращает true, если конструктор имеет модификатор доступа private

  • Свойство IsPublic: возвращает true, если конструктор имеет модификатор доступа public

  • Метод GetMethodBody(): возвращает тело конструктора в виде объекта MethodBody

  • Метод GetParameters(): возвращает массив параметров, где каждый параметр представлен объектом типа ParameterInfo

  • Метод Invoke(): вызывает конструктор

Исследуем конструкторы

using System.Reflection;

Type myType = typeof(Person);

Console.WriteLine("Конструкторы:");
foreach (ConstructorInfo ctor in myType.GetConstructors(
    BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
{
    string modificator = "";

    // получаем модификатор доступа
    if (ctor.IsPublic)
        modificator += "public";
    else if (ctor.IsPrivate)
        modificator += "private";
    else if (ctor.IsAssembly)
        modificator += "internal";
    else if (ctor.IsFamily)
        modificator += "protected";
    else if (ctor.IsFamilyAndAssembly)
        modificator += "private protected";
    else if (ctor.IsFamilyOrAssembly)
        modificator += "protected internal";

    Console.Write($"{modificator} {myType.Name}(");
    // получаем параметры конструктора
    ParameterInfo[] parameters = ctor.GetParameters();
    for (int i = 0; i < parameters.Length; i++)
    {
        var param = parameters[i];
        Console.Write($"{param.ParameterType.Name} {param.Name}");
        if (i < parameters.Length - 1) Console.Write(", ");
    }
    Console.WriteLine(")");
}
class Person
{
    public string Name { get; }
    public int Age { get; }
    public Person(string name, int age)
    {
        Name = name; Age = age;
    }
    public Person(string name) : this(name, 1) { }
    private Person() : this("Tom") { }
}

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

Конструкторы:
public Person(String name, Int32 age)
public Person(String name)
private Person()
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850