Генерация MSIL
Механизм рефлексии в .NET
В .NET предусмотрен специальный механизм доступа к метаданным приложения, который называется рефлексия . С помощью рефлексии мы можем получить доступ к полной информации о типах данных приложения во время исполнения, а также можем создавать новые типы данных и исполняемый код. Сначала мы изучим, каким образом можно получить доступ к уже существующим метаданным.
С помощью методов класса Reflection по данному объекту или его имени можно получить значение типа Type , которое содержит практически исчерпывающую информацию об этом классе - в частности, можно получить его список полей, методов, непосредственного предка данного класса, интерфейсы, которые реализованы в этом объекте и т.д. Всю эту информацию можно хранить и обрабатывать с помощью специальных классов, таких, как MethodInfo , FieldInfo и т.п. Таким образом, для данной сборки можно получить список ее модулей, у которых можно получить список типов и так далее до любых подробностей устройства объекта (вплоть до конкретного бинарного кода его методов, разумеется, только при наличии соответствующих прав доступа).
Отметим, что не все классы, имеющие отношение к рефлексии находятся в пространстве имен Reflection , например, сам класс Type находится в пространстве имен System . Такая "нелогичность" объясняется тем, что класс Type используется не только в ситуациях, связанных с рефлексией. Например, параметром метода ToArray класса ArrayList является значение типа Type , задающее тип элемента массива.
Поскольку рефлексия является сложной и объемной темой, мы продемонстрируем этот механизм на небольшом примере, в котором реализована функция, позволяющая распечатать объект практически любого типа в виде, пригодном для чтения человеком. Схожий пример ( MetaInfo ) можно также найти в примерах, входящих в состав .NET SDK.
Пример на рефлексию
// Пример, демонстрирующий использование рефлексии
// Автор: Антон Москаль,
// Санкт-Петербургский государственный университет, 2001
using System;
using System.Text;
using System.Reflection;
class Sample
{
public struct Struct { public String name; public int[] vec; }
public static void Main (String[] args)
{
Struct s;
s.name = "Name";
s.vec = new int [2];
s.vec [0] = 1;
s.vec [1] = 4;
Console.WriteLine (Refl.Repr(s));
}
}На данном слайде приведена программа, вызывающая упомянутую выше функцию распечатки объекта. Результатом работы данного примера будет следующая строка:
STRUCT{name:Name, vec:[2]{1, 4}}Рассмотрим реализацию функции класса Refl :
class Refl
{
public static string Repr (Object o)
{
// получаем дескриптор типа o
Type t = o.GetType ();
// если o - массив:
if (t.IsArray)
return ReprArray ((Array) o);
// если o - структура (value type, не являющийся встроенным типом):
else if (t.IsValueType && !t.IsPrimitive)
return ReprStruct (t, o);
// в противном случае используем стандартную фукнцию ToString:
else
return o.ToString ();
}
public static string ReprArray (Array a)
{
string res = "["+a.Length+"]{";
// и дальше в цикле добавляем в res
// список представлений его элементов:
String sep = "";
for (int i = 0; i != a.Length; ++i, sep = ", ")
// для бестипового массива индексацию почему-то использовать
// нельзя, поэтому приходится пользоваться функцией GetValue:
res += sep + Repr (a.GetValue (i));
return res + "}";
}
public static string ReprStruct (Type t, Object o)
{
// получаем массив дескрипторов полей:
FieldInfo [] flds = t.GetFields ();
string res = "STRUCT{";
String sep = "";
// в цикле добавляем представления полей в виде
// <имя поля>: <значение поля>
foreach (FieldInfo fld in flds)
{
// метод GetValue в дескриторе поля
// позволяет выбрать это поле из объекта:
res += sep + fld.Name + ":" + Repr (fld.GetValue (o));
sep = ", ";
}
return res + "}";
}
}