Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка для КР по ООП.doc
Скачиваний:
8
Добавлен:
18.04.2019
Размер:
2.47 Mб
Скачать

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

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

  • обнаружить и идентифицировать объявленные в приложении типы,

  • выявить и идентифицировать члены класса,

  • подготовить при выполнении приложения вызов методов и свойств, доступ к значениям полей-членов класса.

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

  • обнаружить и идентифицировать объявленные в приложении типы,

  • выявить и идентифицировать члены класса,

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

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

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

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

Имя элемента

Описание

CreateInstance

Определяет, что отражение должно создавать экземпляр заданного типа. Вызывает конструктор, соответствующий указанным аргументам. Предоставленное имя пользователя не обрабатывается. Если тип поиска не указан, будут использованы флаги (Instance | Public). Инициализатор типа вызвать нельзя.

DeclaredOnly

Определяет, что должны рассматриваться только члены, объявленные на уровне переданной иерархии типов. Наследуемые члены не учитываются.

Default

Определяет отсутствие флагов связывания.

ExactBinding

Определяет, что типы представленных аргументов должно точно соответствовать типам соответствующих формальных параметров. Если вызывающий оператор передает непустой объект Binder, отражение создает исключение, так как при этом вызывающий оператор предоставляет реализации BindToXXX, которые выберут соответствующий метод.

Отражение моделирует правила доступа для системы общих типов. Например, если вызывающий оператор находится в той же сборке, ему не нужны специальные разрешения относительно внутренних членов. В противном случае вызывающему оператору потребуется 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, и PROPPUTREF и нужно различать вызываемые методы.

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 > 0”);

myField = value;

}

}

}

class MyApp

{

static void Main()

{

// Создали объект-представитель класса MyType

// на основе объявления класса 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);

}

}

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