Россия |
Генерация 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 + "}"; } }