1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 85
Текст из файла (страница 85)
Создав объекткласса Assembly, можно получить содержащуюся в нем информацию о типах с помощьюметода GetTypes(). Формат его вызова таков:Туре[] GetTypes()Этот метод возвращает массив типов, содержащихся в компоновочном файле.Чтобы продемонстрировать получение информации о типах из компоновочногофайла, нужно иметь два файла. Первый должен включать набор классов. Поэтому создадимфайл MyClasses.cs с таким содержимым:// Этот файл содержит три класса.// Назовите его MyClasses.cs.466Часть I.
Язык C#usingclassintintSystem;MyClass {x;y;public MyClass(int i) {Console.WriteLine("Создание объекта по формату MyClass(int). ");x = y = i;show();}public MyClass(int i, int j) {Console.WriteLine("Создание объекта по формату MyClassdnt, 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 AnotherClass {string remark;public AnotherClass(string str) {remark = str;}Глава 17.
Динамическая идентификация типов, отражение и атрибуты467}public void show() {Console.WriteLine(remark);}class Demo {public static void Main() {Console.WriteLine("Это заглушка.");}}Этот файл содержит класс MyClass, который мы использовали в предыдущихпримерах. Кроме того, сюда входит класс AnotherClass и еще один класс — Demo.Таким образом, компоновочный файл, генерируемый программой, должен содержать трикласса. Теперь скомпилируем этот файл, чтобы получить файл MyClasses.exe.
Это иесть компоновочный файл, который мы будем опрашивать.Теперь рассмотрим программу, которая извлекает информацию о файлеMyClasses.ехе./* Находим компоновочный файл, определяем типы исоздаем объект, используя средство отражения. */using System;using System.Reflection;class ReflectAssemblyDemo {public static void Main() {int val;// Загружаем компоновочный файл MyClasses.exe.Assembly asm = Assembly.LoadFrom("MyClasses.exe");// Узнаем, какие типы содержит файл MyClasses.exe.Type[] alltypes = asm.GetTypes();foreach(Type temp in alltypes)Console.WriteLine("Обнаружено: " + temp.Name);Console.WriteLine();// Используем первый тип,// которым в данном случае является MyClass,Type t = alltypes[0];// Анализируем первый// обнаруженный класс.Console.WriteLine("Используем: " + t.Name);// Получаем информацию о конструкторах.ConstructorInfo[] ci = t.GetConstructors();Console.WriteLine("Имеются следующие конструкторы: ");foreach(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 +468Часть I.
Язык C#" " + 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] = 18;m.Invoke(reflectOb, args);}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) {Глава 17.
Динамическая идентификация типов, отражение и атрибуты469val = (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Обнаружено: AnotherClassОбнаружено: DemoИспользуем: MyClassИмеются следующие конструкторы: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Как видно из результатов выполнения программы, были обнаружены все три класса,содержащиеся в файле MyClasses.exe. Первый из выявленных классов, в данном случаеэто MyClass, был использован для реализации объекта и выполнения его методов. Причемвсе это было проделано без использования информации о содержимом файлаMyClasses.ехе.Информация о типах, содержащихся в файле MyClasses.exe, извлекается спомощью следующей последовательности инструкций, которыми открывается методMain():// Загружаем компоновочный файл MyClasses.exe.Assembly asm = Assembly.LoadFrom("MyClasses.ехе");// Узнаем, какие типы содержит файл MyClasses.exe.Туре[] alltypes = asm.GetTypes();foreach(Type temp in alltypes)Console.WriteLine("Обнаружено: " + temp.Name);470Часть I. Язык C#Эту последовательность инструкций можно использовать в случае, когда нужнодинамически загрузить и опросить компоновочный файл.Кстати, компоновочный файл необязательно должен быть exe-файлом,Компоновочные файлы также можно найти среди файлов динамически подключаемойбиблиотеки (dynamic link library — DLL), которые имеют расширение dll.
Например, файлMyClasses.cs можно скомпилировать с помощью такой командной строки:csc /t:library MyClasses.csВ результате выполнения этой команды мы получили бы выходной файлMyClasses.dll. При внесении программного кода в DLL-библиотеку не требуетсясоздавать метод Main(). Для всех exe-файлов наличие такой точки входа, как методMain(), обязательно. Поэтому класс Demo содержит заглушку для метода Main(). ДляDLL-библиотек точки входа могут отсутствовать. Если вы захотите превратить MyClass вDLL-файл, вам придется изменить обращение к методу LoadFrom() следующим образом:Assembly asm = Assembly.LoadFrom("MyClasses.dll");Полная автоматизация получения информации о типахПрежде чем завершить изучение темы отражения информации о типах, стоитрассмотреть еще один пример.
Несмотря на то что в предыдущей программе нам удалосьиспользовать класс MyClass без явного указания его имени в программе, все же мыпредварительно знали содержимое класса MyClass. Например, нам были заранее известныимена его методов (set() и sum()). Однако с помощью средства отражения можноиспользовать тип, о котором нам предварительно ничего не известно. Для этого необходимополучить информацию, необходимую для создания объекта, и сгенерировать вызовыметодов. Такой подход эффективен, например, в случае визуального средствапроектирования, поскольку в нем используются типы, имеющиеся в системе.Чтобы понять, как динамически извлечь информацию о типе, рассмотрим следующийпример, в котором загружается компоновочный файл MyClasses.exe, создается объекткласса MyClass, а затем вызываются все объявленные методы без каких бы то ни былопредварительных сведений.// Использование класса MyClass без опоры на// предварительные данные о нем.using System;using System.Reflection;class ReflectAssemblyDemo {public static void Main() {int val;Assembly asm = Assembly.LoadFrom("MyClasses.exe");Type[] alltypes = asm.GetTypes();Type t = alltypes[0];// Используем первый// обнаруженный класс.Console.WriteLine("Используем: " + t.Name);ConstructorInfo[] ci = t.GetConstructors();// Используем первый обнаруженный конструктор.Глава 17.
Динамическая идентификация типов, отражение и атрибуты471ParameterInfo[] cpi = ci[0].GetParameters();object reflectOb;if(cpi.Length > 0) {object[] consargs = new object[cpi.Length];// Инициализируем аргументы.for(int n=0; n < cpi.Length; n++)consargs[n] = 10 + n * 20;// Создаем объект.reflectOb = ci[0].Invoke(consargs);} elsereflectOb = ci[0].Invoke(null);Console.WriteLine("\nВызываем методы для объекта reflectOb.");Console.WriteLine();// Игнорируем унаследованные методы.MethodInfo[] mi = t.GetMethods(BindingFlags.DeclaredOnly |BindingFlags.Instance |BindingFlags.Public);// Вызываем каждый метод.foreach(MethodInfo m in mi) {Console.WriteLine("Вызов метода {0} ", m.Name);// Получаем параметры.ParameterInfo[] pi = m.GetParameters();// Выполняем методы.switch(pi.Length) {case 0: // без аргументовif(m.ReturnType == typeof(int)) {val = (int) m.Invoke(reflectOb, null);Console.WriteLine("Результат равен " + val);}else if(m.ReturnType == typeof(void)) {m.Invoke(reflectOb, null);}break;case 1: // один аргументif(pi[0].ParameterType == typeof(int)) {object[] args = new object[1];args[0] = 14;if((bool) m.Invoke(reflectOb, args))Console.WriteLine("14 находится между x и y.");elseConsole.WriteLine("14 не находится между x и y.");}break;case 2: // два аргументаif((pi[0].ParameterType == typeof(int)) &&472Часть I.
Язык C#(pi[1].ParameterType == typeof(int))) {object[] args = new object[2];args[0] = 9;args[1] = 18;m.Invoke(reflectOb, args);}else if((pi[0].ParameterType == typeof(double)) &&(pi[1].ParameterType == typeof(double))) {object[] args = new object[2];args[0] = 1.12;args[1] = 23.4;m.Invoke(reflectOb, args);}break;}}}}Console.WriteLine();Результаты выполнения этой программы таковы:Используем: MyClassСоздание объекта по формату MyClass(int).Значение x: 10, значение y: 10Вызываем методы для объекта reflectOb.Вызов метода sumРезультат равен 20Вызов метода isBetween14 не находится между x и y.Вызов метода setВнутри метода set(int, int). Значение x: 9, значение y: 18Вызов метода setВнутри метода set(double, double).