Атрибуты, сборки, рефлексия
Реализация отражения. Type, InvokeMember, BindingFlags
Сначала – определения.
Раннее (статическое) связывание – деятельность, выполняемая на стадии компиляции и позволяющая:
- обнаружить и идентифицировать объявленные в приложении типы;
- выявить и идентифицировать члены класса;
- подготовить при выполнении приложения вызов методов и свойств, доступ к значениям полей – членов класса.
Позднее (динамическое) связывание – деятельность, выполняемая непосредственно при выполнении приложения и позволяющая:
- обнаружить и идентифицировать объявленные в приложении типы;
- выявить и идентифицировать члены класса;
- обеспечить в ходе выполнения приложения вызов методов и свойств, доступ к значениям полей – членов класса.
При этом вызов методов и свойств при выполнении приложения обеспечивается методом InvokeMember. Этот метод выполняет достаточно сложную работу и поэтому нуждается в изощренной системе управления, для реализации которой применяется перечисление BindingFlags. В рамках этого перечисления определяются значения флажков, которые управляют процессом динамического связывания в ходе реализации отражения.
Перечисление также применяется для управления методом GetMethod.
Список элементов перечисления прилагается.
Далее демонстрируется применение класса 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.
Ну вот. Создавали объект, изменяли значение его поля (данного-члена), вызывали его (нестатический) метод, обращались к свойству (подсовывали ему некорректные значения). И при этом НИ РАЗУ НЕ НАЗЫВАЛИ ВЕЩИ СВОИМИ ИМЕНАМИ! В сущности, ЭТО И ЕСТЬ ОТРАЖЕНИЕ.