Рефлексия конструкторов, полей и свойств
25C# --- Сборки .NET --- Рефлексия конструкторов, полей и свойств
В предыдущей статье мы рассмотрели пример использования рефлексии методов, таким же образом можно получить информацию о полях, свойствах и интерфейсах класса с помощью методов GetField() и GetInterfaces. Давайте модифицируем программу из предыдущей статьи и добавим обобщенные методы в класс Reflect, отображающие информацию о полях, свойствах и интерфейсах, добавив реализацию интерфейса в класс MyTestClass:
...
interface IInfoClass
{
double Sum();
void Info();
void Set(double d1, double d2);
}
// Тестовый класс, содержащий некоторые конструкции
class MyTestClass : IInfoClass
{
...
// В данном классе определены методы использующие рефлексию
class Reflect
{
// Информация о полях и реализуемых интерфейсах
public static void FieldInterfaceInfo<T>(T obj) where T : class
{
Type t = typeof(T);
Console.WriteLine("\n*** Реализуемые интерфейсы ***\n");
var im = t.GetInterfaces();
foreach (Type tp in im)
Console.WriteLine("--> "+tp.Name);
Console.WriteLine("\n*** Поля и свойства ***\n");
FieldInfo[] fieldNames = t.GetFields();
foreach (FieldInfo fil in fieldNames)
Console.Write("--> "+fil.ReflectedType.Name + " " + fil.Name + "\n");
}
// Данный метод выводит информацию о содержащихся в классе методах
public static void MethodReflectInfo<T> (T obj) where T: class
{
Type t = typeof(T);
// Получаем коллекцию методов
MethodInfo[] MArr = t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
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);
Reflect.FieldInterfaceInfo<MyTestClass>(mtc);
Console.ReadLine();
}
}
Следует иметь в виду, что большинство из "получающих", т.е. get-методов в System.Type (GetMethods(), GetInterfaces() и т.д.), имеют перегруженные версии, принимающие значения из перечисления BindingFlags. Это позволяет более точно указать, поиск чего должен производиться (например, только статических членов, только общедоступных членов, включая приватные члены, и т.д.).
Сильные стороны рефлексии проявляются наиболее заметно лишь в том случае, если объект создается динамически во время выполнения. И для этого необходимо получить сначала список конструкторов, а затем экземпляр объекта заданного типа, вызвав один из этих конструкторов. Такой механизм позволяет получать во время выполнения экземпляр объекта любого типа, даже не указывая его имя в операторе объявления.
Конструкторы конкретного типа получаются при вызове метода GetConstructors() для объекта класса Type. Ниже приведена одна из наиболее часто используемых форм этого метода:
ConstructorInfо[] GetConstructors()
Метод GetConstructors() возвращает массив объектов класса ConstructorInfо, описывающих конструкторы. Класс ConstructorInfo является производным от абстрактного класса MethodBase, который в свою очередь наследует от класса MemberInfo. В нем также определен ряд собственных методов. К их числу относится интересующий нас метод GetConstructors(), возвращающий список параметров, связанных с конструктором. Этот метод действует таким же образом, как и упоминавшийся ранее метод GetParameters(), определенный в классе MethodInfo.
Как только будет обнаружен подходящий конструктор, для создания объекта вызывается метод Invoke(), определенный в классе ConstructorInfo. Ниже приведена одна из форм этого метода:
object Invoke(object[] parameters)
Любые аргументы, которые требуется передать методу, указываются в массиве parameters. Если же аргументы не нужны, то вместо массива parameters указывается пустое значение (null). Но в любом случае количество элементов массива parameters должно совпадать с количеством передаваемых аргументов, а типы аргументов — с типами параметров. Метод Invoke() возвращает ссылку на сконструированный объект.