1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 84
Текст из файла (страница 84)
Значение x: 9, значение y: 18Внутри метода set(double, double). Значение x: 1, значение y: 23Значение x: 1, значение y: 23Обратите внимание на то, как организуется вызов методов. Сначала получаем списокметодов. Затем в цикле fоreach извлекаем информацию о параметрах каждого метода.После этого, используя последовательность if/else-инструкций, вызываем каждый методс соответствующим количеством параметров определенного типа. Особое вниманиеобратите на способ вызова перегруженного метода set():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);}Глава 17. Динамическая идентификация типов, отражение и атрибуты461else 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);}Если имя метода совпадает со строкой set, проверяется тип первого параметра, чтобыопределить версию метода set().
Если окажется, что рассматривается версия set(int,int), в массив args загружаются int-аргументы и вызывается метод set(). Впротивном случае для вызова метода set() используются аргументы типа double.Получение конструкторов типаВ предыдущем примере продемонстрирована возможность вызова методов сиспользованием средстве отражения, однако такой подход не имеет преимуществ посравнению с непосредственным вызовом методов (в данном случае класса MyClass),поскольку объект типа MyClass создается явным образом. Другими словами, прощевызывать эти методы обычным способом.
Однако мощь отражения начинает проявляться втех случаях, когда объект создается динамически во время работы программы. Для этогонужно сначала получить список конструкторов. Затем создать экземпляр типа, вызвав одиниз конструкторов. Этот механизм позволяет реализовать объект любого типа во времяработы программы, не называя его в инструкции объявления.Чтобы получить конструкторы типа, вызовите метод GetConstructors() дляобъекта класса Туре. Один из наиболее употребительных форматов его вызова таков:ConstructorInfo[] GetConstructors()Он возвращает массив объектов типа ConstructorInfo, которые описывают этиконструкторы.Класс ConstructorInfо выведен из абстрактного класса MethodBase, которыйявляется производным от класса MemberInfo.
Класс ConstructorInfo определяеттакже собственные члены. Из них нас интересует прежде всего метод GetParameters(),который возвращает список параметров, связанных с конструктором. Он работает подобнометоду GetParameters(), определенному в описанном выше классе MethodInfо.Получив информацию о конструкторе, можно с его помощью создать объект, вызвавметод Invoke(), определенный в классе ConstructorInfo. Формат вызова методаInvoke() в этом случае таков:object Invoke(object[] args)Любые аргументы, которые необходимо передать конструктору, задаются с помощьюмассива args. Если конструктор вызывается без аргументов, параметр args должен иметьnull-значение. При этом длина массива args должна в точности совпадать с количествомаргументов.
Метод Invoke() возвращает ссылку на создаваемый объект.В следующей программе демонстрируется использование средства отражения длясоздания экземпляра класса MyClass:// Создание объекта с помощью средства отражения.using System;using System.Reflection;462Часть I. Язык C#class MyClass {int x;int y;public MyClass(int i) {Console.WriteLine("Создание объекта по формату MyClass(int).
");x = y = i;}public MyClass(int i, int j) {Console.WriteLine("Создание объекта по формату MyClass(int, int). ");x = i;y = j;show();}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 InvokeConsDemo {public static void Main() {Type t = typeof(MyClass);int val;// Получаем информацию о конструкторах.ConstructorInfo[] ci = t.GetConstructors();Console.WriteLine("Имеются следующие конструкторы: ");Глава 17. Динамическая идентификация типов, отражение и атрибуты463foreach(ConstructorInfo c in ci) {// Отображаем тип возвращаемого значения и имя.Console.Write(" " + t.Name + "{");// Отображаем параметры.ParameterInfo[] pi = c.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();// Находим подходящий конструктор.int x;for(x=0; x < ci.Length; x++) {ParameterInfo[] pi = ci[x].GetParameters();if(pi.Length == 2) break;}if(x == ci.Length) {Console.WriteLine("Подходящий конструктор не найден.");return;}elseConsole.WriteLine("Найден конструктор с двумя параметрами.\n");// Создаем объект.object[] consargs = new object[2];consargs[0] = 10;consargs[1] = 20;object reflectOb = ci[x].Invoke(consargs);Console.WriteLine("\nВызов методов для объекта reflectOb.");Console.WriteLine();MethodInfo[] mi = t.GetMethods();// Вызываем каждый метод.foreach(MethodInfo m in mi) {// Получаем параметры.ParameterInfo[] pi = m.GetParameters();if(m.Name.CompareTo("set")==0 &&pi[0].ParameterType == typeof(int)) {// Это метод set(int, int).object[] args = new object[2];args[0] = 9;args[1] = 13;m.Invoke(reflectOb, args);}464Часть I.
Язык C#}}}else if(m.Name.CompareTo("set")==0 &&pi[0].ParameterType == typeof(double)) {// Это метод set(double, 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(Int32 i)MyClass(Int32 i, Int32 j)Найден конструктор с двумя параметрами.Создание объекта по формату MyClass(int, int).Значение x: 10, значение y: 20Вызов методов для объекта reflectOb.Результат выполнения метода sum() равен 3014 находится между x и y.Внутри метода set(int, int).
Значение x: 9, значение y: 18Внутри метода set(double, double). Значение x: 1, значение y: 23Значение x: 1, значение y: 23Теперь разберемся, как используется средство отражения для создания объектакласса MyClass. Сначала с помощью следующей инструкции получаем список открытыхконструкторов:ConstructorInfo[] ci = t.GetConstructors();Затем в целях иллюстрации отображаем конструкторы, определенные в этом классе.После этого с помощью следующего кода просматриваем полученный список, чтобы найтиконструктор, который принимает два аргумента:for(x=0; x < ci.Length; x++) {ParameterInfo[] pi = ci(x).GetParameters();if(pi.Length == 2) break;}Если нужный конструктор обнаружен (как в данном случае), создаем объект,выполнив такую последовательность инструкций:Глава 17.
Динамическая идентификация типов, отражение и атрибуты465// Создаем объект.object[] consargs = new object[2];consargs[0) = 10;consargs[1] = 20;object reflectOb = ci[x].Invoke(consargs);После обращения к методу Invoke() объект reflectOb будет ссылаться наобъект класса MyClass.В этом примере в целях упрощения предполагалось, что конструктор, которыйпринимает два int-аргумента, — единственный среди всех конструкторов, определенных вклассе MyClass. В реальных приложениях необходимо проверять тип каждого аргумента.Получение типов из компоновочных файловВ предыдущем примере с помощью средства отражения мы многое узнали о классеMyClass, но не все: мы не получили данные о самом типе MyClass. Несмотря на то чтомы динамически извлекли из соответствующих объектов информацию о типе MyClass, мыисходили из того, что нам заранее было известно имя типа MyClass, и использовалиинструкцию typeof для получения объекта класса Туре, для которого вызывались всеметоды средства отражения (напрямую или опосредованно).
И хотя в некоторых ситуацияхтакой подход себя вполне оправдывает, возможности средства отражения проявляются вполной мере тогда, когда программа в состоянии определить необходимые типыпосредством анализа содержимого других компоновочных файлов.Как было описано в главе 16, компоновочный файл включает информацию о классах,структурах и пр., которые он содержит. Интерфейс Reflection API позволяет загрузитькомпоновочный файл, извлечь информацию о нем и создать экземпляры любого изсодержащихся в нем типов. Используя этот механизм, программа может проанализироватьсреду выполнения и заставить ее поработать в нужном направлении, не определяя явнымобразом “точки приложения” во время компиляции. Это чрезвычайно эффективноесредство.
Например, представьте себе программу, которая действует как браузер типов,отображая доступные в системе типы. Или представьте другое приложение, котороевыполняло бы роль средства проектирования, позволяющего визуально связыватьотдельные части программы, состоящей из различных типов, поддерживаемых системой.Если все данные о типе поддаются обнаружению, то не существует ограничений наприменение средства отражения.Для получения информации о компоновочном файле сначала необходимо создатьобъект класса Assembly.
Класс Assembly не определяет ни одного publicконструктора. Но объект класса Assembly можно создать, вызвав один из его методов.Например, воспользуемся методом LoadFrom(). Вот формат его использования:static Assembly LoadFrom(string имя_файла)Здесь элемент имя_файла означает имя компоновочного файла.