Опубликован: 23.07.2006 | Доступ: свободный | Студентов: 2213 / 889 | Оценка: 4.28 / 4.17 | Длительность: 21:37:00
Специальности: Системный архитектор
Лекция 14:

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