1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 83
Текст из файла (страница 83)
Это чрезвычайно мощное средство, поскольку,получив информацию о типе, можно вызвать конструкторы этого типа, его методы ииспользовать его свойства. Таким образом, отражение позволяет использовать код, которыйнедоступен в период компиляции программы.Программный интерфейс Reflection API —довольно обширная тема, и здесьневозможно рассмотреть ее в полном объеме. (Полное описание Reflection API — этоматериал для целой книги!) Но интерфейс Reflection API столь логичен, что, поняв, какиспользовать одну его часть, нетрудно разобраться во всем остальном. В следующихразделах описаны ключевые способы применения средства отражения: получениеинформации о методах, вызов методов, создание объектов и загрузка типов изкомпоновочных файлов.Получение информации о методахС помощью Type-объекта можно получить список методов, поддерживаемыхзаданным типом.
Для этого используется метод GetMethods(). Один из форматов еговызова таков:MethodInfo[] GetMethods()Глава 17. Динамическая идентификация типов, отражение и атрибуты455Метод GetMethods() возвращает массив объектов типа MethodInfo, которыеописывают методы, поддерживаемые вызывающим типом. Класс MethodInfо определен впространстве имен System.Reflection.Класс MethodInfo — производный от абстрактного класса MethodBase, который,в свою очередь, выведен из класса MemberInfo. Таким образом, программисту доступнысвойства и методы, определенные во всех трех классах. Например, чтобы получить имяметода, используйте свойство Name. Особого внимания заслуживают два члена классаMethodInfo: ReturnType и GetParameters().Свойство ReturnType, которое имеет тип Туре, позволяет получить тип значения,возвращаемого методом.Метод GetParameters() возвращает список параметров, связанных с методом.Формат его вызова таков:ParameterInfo[] GetParameters()Информация о параметрах содержится в объекте класса ParameterInfo.
В классеParameterInfo определено множество свойств и методов, которые используются дляописания параметров. Из них стоит обратить внимание на следующие два: свойство Name,которое представляет собой строку, содержащую имя параметра, и свойствоParameterType, которое описывает тип параметра. Тип параметра инкапсулирован вобъекте класса Туре.Рассмотрим программу, в которой средство отражения используется для полученияметодов, поддерживаемых классом MyClass.
Для каждого метода программа отображаетего имя и тип возвращаемого им значения, а также имя и тип всех параметров, которыеможет иметь тот или иной метод.// Анализ методов с помощью средства отражения.using System;using System.Reflection;class MyClass {int x;int y;public MyClass(int i, int j) {x = i;y = j;}public int sum() {return x+y;}public bool isBetween(int i) {if(x < i && i < y) return true;else return false;}public void set(int a, int b) {x = a;y = b;}public void set(double a, double b) {x = (int) a;y = (int) b;456Часть I.
Язык C#}}public void show() {Console.WriteLine(" x: {0}, y: {1}", x, y);}class ReflectDemo {public static void Main() {Type t = typeof(MyClass); // Получаем Type-объект,// представляющий MyClass.Console.WriteLine("Анализ методов, определенных в " + t.Name);Console.WriteLine();Console.WriteLine("Поддерживаемые методы: ");MethodInfo[] mi = t.GetMethods();// Отображаем методы, поддерживаемые классом MyClass.foreach(MethodInfo m in mi) {// Отображаем тип значения, возвращаемого методом,// и имя метода.Console.Write(" " + m.ReturnType.Name +" " + m.Name + "(");// Отображаем параметры.ParameterInfo[] pi = m.GetParameters();for(int i=0; i < pi.Length; i++) {Console.Write(pi[i].ParameterType.Name +" " + pi[i].Name);if(i+1 < pi.Length) Console.Write(", ");}Console.WriteLine(")");}}}Console.WriteLine();Результаты выполнения этой программы такие:Анализ методов, определенных в MyClassПоддерживаемые методы:Int32 GetHashCode()Boolean Equals(Object obj)String ToString()Int32 sum()Boolean isBetween(Int32 i)Void set(Int32 a, Int32 b)Глава 17.
Динамическая идентификация типов, отражение и атрибуты457Void set(Double a, Double b)Void show()Type GetType()Обратите внимание на то, что помимо методов, определенных в классе MyClass,здесь также отображаются методы, определенные в классе object. Дело в том, что всетипы в C# выведены из класса object. Также стоит отметить, что для имен типов здесьиспользуются имена .NET-структуры.
Обратите внимание еще на то, что метод set()отображен дважды. Этому есть простое объяснение: метод set() перегружен. Одна еговерсия принимает аргументы типа int, а вторая — аргументы типа double.Эта программа требует некоторых пояснений. Прежде всего, отметим, что в классеMyClass определяется public-конструктор и ряд public-методов, включаяперегруженный метод set(). При выполнении строки кода из метода Main() получаемобъект класса Туре, представляющий класс MyClass:Type t = typeof(MyClass);// Получаем Type-объект,// представляющий MyClass.Используя переменную t и интерфейс Reflection API, программа отображаетинформацию о методах, поддерживаемых классом MyClass. Сначала с помощьюследующей инструкции получаем список методов:MethodInfo[] mi = t.GetMethods();Затем выполняется цикл foreach, на итерациях которого для каждого методаотображается тип возвращаемого им значения, имя метода и его параметры:// Отображаем тип значения, возвращаемого методом,// и имя метода.Console.Write(" " + m.ReturnType.Name +" " + m.Name + " (");// Отображаем параметры.ParameterInfo[] pi = m.GetParameters();for(int i=0; i < pi.Length; i++) {Console.Write(pi[i].ParameterType.Name +" " + pi[i].Name);if(i+1 < pi.Length)Console.Write(", ");}В этом фрагменте программы информация о параметрах каждого метода считываетсяпосредством вызова метода GetParameters() и сохраняется в массиве pi.
Затем в циклеfor выполняется опрос массива pi и отображается тип каждого параметра и его имя.Главное здесь то, что эта информация считывается динамически во время выполненияпрограммы, т.е. без предварительной информации о классе MyClass.Второй формат вызова метода GetMethods()Второй формат вызова метода GetMethods() позволяет задать различные флаги,которые фильтруют возвращаемые методы.
Этот формат таков:MethodInfo[] GetMethods(BindingFlags flags)Эта версия получает только те методы, которые соответствуют заданному критерию.BindingFlags — это перечисление. Ниже описаны наиболее употребительные егозначения:458Часть I. Язык C#ЗначениеОписаниеDeclaredOnlyСчитывание только тех методов, которые определены в заданном классе.Унаследованные методы в результат не включаютсяСчитывание методов экземпляровСчитывание нe-public-методовСчитывание public-методовСчитывание static-методовInstanceNonpublicPublicstaticДва или больше задаваемых флагов можно объединять с помощью оператора ИЛИ.
Сфлагами Public или Nonpublic необходимо устанавливать флаги Instance илиStatic. В противном случае метод GetMethods() не возвратит ни одного метода.Одно из основных применений BindingFlags-формата, используемого при вызовеметода GetMethods(), — получение списка определенных в классе методов, но без учетаунаследованных. Этот вариант особенно полезен в случае, когда нам не нужна информацияо методах, определенных объектом. Попробуем, например, заменить вызов методаGetMethods() в предыдущей программе таким вариантом:// Теперь получим только те методы, которые объявлены// в классе MyClass.MethodInfo[] mi = t.GetMethods(BindingFlags.DeclaredOnly |BindingFlags.Instance |BindingFlags.Public);После внесения в программу этого изменения получаем следующие результаты:Анализ методов, определенных в MyClassПоддерживаемые методы:Int32 sum()Boolean isBetween(Int32 i)Void set(Int32 a, Int32 b)Void set(Double a, Double b)Void show()Как видите, теперь отображены только те методы, которые явно определены в классеMyClass.Вызов методов с помощью средства отраженияЗная, какие методы поддерживает тип, можно вызвать любой из них.
Для этогоиспользуется метод Invoke(), который определен в классе MethodInfo. Формат еговызова таков:object Invoke(object ob, object[] args)Здесь параметр ob — это ссылка на объект, для которого вызывается нужный метод.Для static-методов параметр ob должен содержать значение null. Любые аргументы,которые необходимо передать вызываемому методу, указываются в массиве args. Еслиметод вызывается без аргументов, параметр args должен иметь null-значение. При этомдлина массива args должна совпадать с количеством аргументов, передаваемых методу.Следовательно, если необходимо передать два аргумента, массив args должен состоять издвух элементов, а не, например, из трех или четырех.Глава 17.
Динамическая идентификация типов, отражение и атрибуты459Для вызова нужного метода достаточно вызвать метод Invoke() для экземпляракласса MethodInfo, полученного в результате вызова метода GetMethods(). Этапроцедура демонстрируется следующей программой:// Вызов методов с использованием средства отражения.using System;using System.Reflection;class MyClass {int x; int y;public MyClass(int i, int j) {x = i;y = j;}public int sum() {return x + y;}public bool isBetween(int i) {if((x < i) && (i < y)) return true;else return false;}public void set(int a, int b) {Console.Write("Внутри метода set(int, int).
");x = a;y = b;show();}// Перегруженный метод set.public void set(double a, double b) {Console.Write("Внутри метода set(double, double). ");x = (int) a;y = (int) b;show();}}public void show() {Console. WriteLine("Значение x: {0}, значение y: {1}", x, y);}class InvokeMethDemo {public static void Main() {Type t = typeof(MyClass);MyClass reflectOb = new MyClass(10, 20);int val;Console.WriteLine("Вызов методов, определенных в " +t.Name);Console.WriteLine();MethodInfo[] mi = t.GetMethods();460Часть I.
Язык C#}}// Вызываем каждый метод.foreach(MethodInfo m in mi) {// Получаем параметры.ParameterInfo[] pi = m.GetParameters();if(m.Name.CompareTo("set")==0 &&pi[0].ParameterType == typeof(int)) {object[] args = new object[2];args[0] = 9;args[1] = 18;m.Invoke(reflectOb, args);}else if(m.Name.CompareTo("set")==0 &&pi[0].ParameterType == typeof(double)) {object[] args = new object[2];args[0] = 1.12; args[1] = 23.4;m.Invoke(reflectOb, args);}else if(m.Name.CompareTo("sum")==0) {val = (int) m.Invoke(reflectOb, null);Console.WriteLine("Результат вызова метода sum равен " + val);}else if(m.Name.CompareTo("isBetween")==0) {object[] args = new object[1];args[0] = 14;if((bool) m.Invoke(reflectOb, args))Console.WriteLine("14 находится между x и y.");}else if(m.Name.CompareTo("show")==0) {m.Invoke(reflectOb, null);}}Результаты выполнения этой программы таковы:Вызов методов, определенных в MyClassРезультат вызова метода sum равен 3014 находится между x и y.Внутри метода set(int, int).