Российский Государственный Технологический Университет им. К.Э. Циолковского
Опубликован: 02.03.2007 | Доступ: свободный | Студентов: 5636 / 790 | Оценка: 3.96 / 3.45 | Длительность: 27:04:00
ISBN: 978-5-9556-0086-4
Лекция 10:

Атрибуты, сборки, рефлексия

Реализация отражения. Type, InvokeMember, BindingFlags

Сначала – определения.

Раннее (статическое) связываниедеятельность, выполняемая на стадии компиляции и позволяющая:

  • обнаружить и идентифицировать объявленные в приложении типы;
  • выявить и идентифицировать члены класса;
  • подготовить при выполнении приложения вызов методов и свойств, доступ к значениям полей – членов класса.

Позднее (динамическое) связываниедеятельность, выполняемая непосредственно при выполнении приложения и позволяющая:

  • обнаружить и идентифицировать объявленные в приложении типы;
  • выявить и идентифицировать члены класса;
  • обеспечить в ходе выполнения приложения вызов методов и свойств, доступ к значениям полей – членов класса.

При этом вызов методов и свойств при выполнении приложения обеспечивается методом InvokeMember. Этот метод выполняет достаточно сложную работу и поэтому нуждается в изощренной системе управления, для реализации которой применяется перечисление BindingFlags. В рамках этого перечисления определяются значения флажков, которые управляют процессом динамического связывания в ходе реализации отражения.

Перечисление также применяется для управления методом GetMethod.

Список элементов перечисления прилагается.

Имя элемента Описание
CreateInstance Определяет, что отражение должно создавать экземпляр заданного типа. Вызывает конструктор, соответствующий указанным аргументам. Предоставленное имя пользователя не обрабатывается. Если тип поиска не указан, будут использованы флаги (Instance | Public). Инициализатор типа вызвать нельзя
DeclaredOnly Определяет, что должны рассматриваться только члены, объявленные на уровне переданной иерархии типов. Наследуемые члены не учитываются
Default Определяет отсутствие флагов связывания
ExactBinding Определяет, что типы представленных аргументов должны точно соответствовать типам соответствующих формальных параметров. Если вызывающий оператор передает непустой объект Binder, отражение создает исключение, так как при этом вызывающий оператор предоставляет реализации BindToXXX, которые выберут соответствующий метод
ChangeType Отражение моделирует правила доступа для системы общих типов. Например, если вызывающий оператор находится в той же сборке, ему не нужны специальные разрешения относительно внутренних членов. В противном случае вызывающему оператору потребуется ReflectionPermission. Этот метод применяется при поиске защищенных, закрытых и т. п. членов.

Главный принцип заключается в том, что ChangeType должен выполнять только расширяющее преобразование, которое никогда не теряет данных. Примером расширяющего преобразования является преобразование 32-разрядного целого числа со знаком в 64-разрядное целое число со знаком. Этим оно отличается от сужающего преобразования, при котором возможна потеря данных. Примером сужающего преобразования является преобразование 64-разрядного целого числа со знаком в 32-разрядное целое число со знаком.

Связыватель по умолчанию не обрабатывает этот флаг, но пользовательские связыватели используют семантику этого флага

FlattenHierarchy Определяет, что должны быть возвращены статические члены вверх по иерархии. Статические члены — это поля, методы, события и свойства. Вложенные типы не возвращаются
GetField Определяет, что должно возвращаться значение указанного поля
GetProperty Определяет, что должно возвращаться значение указанного свойства
IgnoreCase Определяет, что при связывании не должен учитываться регистр имени члена
IgnoreReturn Используется при COM-взаимодействии для определения того, что возвращаемое значение члена может быть проигнорировано
Instance Определяет, что в поиск должны быть включены члены экземпляра
InvokeMethod Определяет, что метод должен быть вызван. Метод не может быть ни конструктором, ни инициализатором типа
NonPublic Определяет, что в поиск должны быть включены члены экземпляра, не являющиеся открытыми ( public )
OptionalParamBinding Возвращает набор членов, у которых количество параметров соответствует количеству переданных аргументов. Флаг связывания используется для методов с параметрами, у которых есть значения методов, и для функций с переменным количеством аргументов (varargs). Этот флаг должен использоваться только с Type.InvokeMember.

Параметры со значениями по умолчанию используются только в тех вызовах, где опущены конечные аргументы. Они должны быть последними аргументами

Public Определяет, что открытые ( public ) члены должны быть включены в поиск
PutDispProperty Определяет, что для COM-объекта должен быть вызван член PROPPUT. PROPPUT задает устанавливающую свойство функцию, использующую значение. Следует использовать PutDispProperty, если для свойства заданы и PROPPUT, и PROP-PUTREF и нужно различать вызываемые методы
PutRefDispProperty Определяет, что для COM-объекта должен быть вызван член PROPPUTREF. PROPPUTREF использует устанавливающую свойство функцию, использующую ссылку, вместо значения. Следует использовать PutRefDispProperty, если для свойства заданы и PROPPUT, и PROPPUTREF и нужно различать вызываемые методы
SetField Определяет, что должно устанавливаться значение указанного поля
SetProperty Определяет, что должно устанавливаться значение указанного свойства. Для COM-свойств задание этого флага связывания эквивалентно заданию PutDispProperty и PutRefDispProperty
Static Определяет, что в поиск должны быть включены статические члены
SuppressChangeType Не реализован

Далее демонстрируется применение класса Type, в частности варианты использования метода – члена класса Type InvokeMember, который обеспечивает выполнения методов и свойств класса.

using System;
using System.Reflection;
// В классе объявлены поле myField, конструктор, метод String ToString(), свойство.
class MyType 
{
int myField;
public MyType(ref int x)
{
x *= 5;
}

public override String ToString()
{
Console.WriteLine("This is: public override String ToString() method!");
return myField.ToString();
}

// Свойство MyProp нашего класса обладает одной замечательной особенностью:
// значение поля myField объекта - представителя класса MyType не может 
// быть меньше нуля. Если это ограничение нарушается – возбуждается 
// исключение. 
public int MyProp 
{
get 
{
return myField;
}
set 
{ 
if (value < 1) 
throw new ArgumentOutOfRangeException("value", value, "value must be > 1");
myField = value;
}
}
}

class MyApp 
{
static void Main() 
{

// Создали объект - представитель класса Type
// на основе объявления класса MyType.
Type t = typeof(MyType);	

// А это одномерный массив объектов, содержащий ОДИН элемент.
// В этом массиве будут передаваться параметры конструктору.
Object[] args = new Object[] {8};
Console.WriteLine("The value of x before the constructor is called is {0}.", args[0]);

// Вот таким образом в рамках технологии отражения производится 
// обращение к конструктору. Наш объект адресуется по ссылке obj.
Object obj = t.InvokeMember(
null,
//____________________________
BindingFlags.DeclaredOnly | 
BindingFlags.Public |
BindingFlags.NonPublic | 
BindingFlags.Instance |
BindingFlags.CreateInstance, // Вот распоряжение о создании объекта... 
//____________________________
null,
null,
args // А так организуется передача параметров в конструктор. 
);

Console.WriteLine("Type: " + obj.GetType().ToString());
Console.WriteLine("The value of x after the constructor returns is {0}.", args[0]);
// Изменение (запись и чтение) значения поля myField только что 
// созданного объекта - представителя класса MyType.
// Как известно, этот объект адресуется по
// ссылке obj. Мы сами его по этой ссылке расположили! 
t.InvokeMember(
"myField", // Будем менять значение поля myField...  
//______________________________
BindingFlags.DeclaredOnly | 
BindingFlags.Public |
BindingFlags.NonPublic | 
BindingFlags.Instance |
BindingFlags.SetField, // Вот инструкция по изменению значения поля.
//_______________________________ 
null,
obj, // Вот указание на то, ГДЕ располагается объект... 
new Object[] {5} // А вот и само значение. Оно упаковывается в массив объектов.
);
int v = (Int32) t.InvokeMember(
"myField", 
//______________________________
BindingFlags.DeclaredOnly | 
BindingFlags.Public |
BindingFlags.NonPublic | 
BindingFlags.Instance |
BindingFlags.GetField, // А сейчас мы извлекаем значение поля myField. 
//______________________________
null,
obj, // "Работаем" все с тем же объектом. Значение поля myField
    // присваивается переменной v.
null
);

// Вот распечатали это значение.
Console.WriteLine("myField: " + v);
// "От имени" объекта будем вызывать нестатический метод.
String s = (String) t.InvokeMember(
"ToString", // Имя переопределенного виртуального метода.
//______________________________
BindingFlags.DeclaredOnly | 
BindingFlags.Public |
BindingFlags.NonPublic | 
BindingFlags.Instance |
BindingFlags.InvokeMethod, // Сомнений нет! Вызываем метод!
//______________________________
null,
obj,   // От имени нашего объекта вызываем метод без параметров.
 null
 );

 // Теперь обращаемся к свойству.
 Console.WriteLine("ToString: " + s);
 // Изменение значения свойства. Пытаемся присвоить недозволенное 
 // значение. И посмотрим, что будет...
 // В конце концов, мы предусмотрели перехватчик исключения.
 try 
 {
 t.InvokeMember(
 "MyProp", // Работаем со свойством.
 //______________________________
 BindingFlags.DeclaredOnly | 
BindingFlags.Public |
BindingFlags.NonPublic | 
BindingFlags.Instance |
BindingFlags.SetProperty, // Установить значение свойства.
//______________________________
null,
obj,
new Object[] {0} // Пробуем через обращение к свойству
// установить недозволенное значение.
);
} 
catch (TargetInvocationException e) 
{
// Фильтруем исключения... Реагируем только на исключения типа
// ArgumentOutOfRangeException. Все остальные "проваливаем дальше". 
if (e.InnerException.GetType() !=typeof (ArgumentOutOfRangeException)) throw;
// А вот как реагируем на ArgumentOutOfRangeException.
// Вот так скромненько уведомляем о попытке присвоения запрещенного 
// значения.
Console.WriteLine("Exception! Catch the property set.");
}

t.InvokeMember(
"MyProp", 
//______________________________
BindingFlags.DeclaredOnly | 
BindingFlags.Public |
BindingFlags.NonPublic | 
BindingFlags.Instance |
BindingFlags.SetProperty, // Установить значение свойства. 
//______________________________
null,
obj,
new Object[] {2} // Вновь присваиваемое значение. Теперь ПРАВИЛЬНОЕ.
);

v = (int) t.InvokeMember(
"MyProp", 
BindingFlags.DeclaredOnly | 
BindingFlags.Public |
BindingFlags.NonPublic | 
BindingFlags.Instance |
BindingFlags.GetProperty, // Прочитать значение свойства.
null,
 obj,
 null
 );

 Console.WriteLine("MyProp: " + v);
 }
 }
Листинг 10.1.

Ну вот. Создавали объект, изменяли значение его поля (данного-члена), вызывали его (нестатический) метод, обращались к свойству (подсовывали ему некорректные значения). И при этом НИ РАЗУ НЕ НАЗЫВАЛИ ВЕЩИ СВОИМИ ИМЕНАМИ! В сущности, ЭТО И ЕСТЬ ОТРАЖЕНИЕ.

kewezok kewezok
kewezok kewezok
Елена Шляхт
Елена Шляхт
Объясните плиз в чем отличие а++ от ++а
Почему результат разный?
int a=0, b=0;
Console.WriteLine(a++); //0
Console.WriteLine(++b); //1
a++;
++b;
Console.WriteLine(a); //2
Console.WriteLine(b); //2