Исследование методов и конструкторов с помощью рефлексии
Получение информации о методах
Для получения получении информации отдельно о методах применяется метод 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 и имеет ряд общей функциональности. Некоторые основные свойства и методы:
Свойство IsAssembly: возвращает 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()