программирование. Руководство su P# a n Reference в herbert schildt полное руководство с 0 герберт шилдт
Скачать 3.32 Mb.
|
В качестве примера ниже приведена программа, в которой рефлексия используется для получения методов, поддерживаемых классом MyClass. В этой программе выводится возвращаемый тип и имя каждого метода, а также имена и типы любых параметров, которые может иметь каждый метод. // Анализ методов с помощью рефлексии. using System; using System.Reflection; class MyClass { int x; int y; public MyClass(int i, int j) { x = i ; У = 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; У = b; } public void Set(double a, double b) { x = (int) a; у = (int) b; } public void Show() { Console.WriteLine(" x: {0}, у: {1}", x, y); } } class ReflectDemo { static void Main() { Type t = typeof(MyClass); // получить объект класса Type, // представляющий класс MyClass Console.WriteLine("Анализ методов, определенных " + "в классе " + t.Name); Console.WriteLine (); Console.WriteLine("Поддерживаемые методы: "); MethodInfo[] mi = t.GetMethods(); // Вывести методы, поддерживаемые в классе MyClass. foreach(Methodlnfo m in mi) { // Вывести возвращаемый тип и имя каждого метода. Console.Write(" " + m.ReturnType.Name + " " + m.Name + "("); // Вывести параметры. Parameterlnfo[] pi = m.GetParameters() ; for(int i=0; i < pi.Length; i++) { Console.Write(pi[i].ParameterType.Name + " " + pi[i].Name); if(i+l < pi.Length) Console.Write(", "); } Console.WriteLine(")"); Console.WriteLine(); } } } Эта программа дает следующий результат. Поддерживаемые методы: Int32 Sum() Boolean IsBetween (Int32 i) Void Set(Int32 a, Int32 b) Void Set (Double a, Double b)' Void Show() String ToString() Boolean Equals(Object ob j ) Int32 GetHashCode() Type GetType() Как видите, помимо методов, определенных в классе MyClass, в данной программе выводятся также методы, определенные в классе ob j ect, поскольку все типы данных в C# наследуют от класса ob j ect. Кроме того, в качестве имен типов указываются имена структуры .NET. Обратите также внимание на то, что метод Set () выводится дважды, поскольку он перегружается. Один из его вариантов принимает аргументы типа int, а другой – аргументы типа double. Рассмотрим эту программу более подробно. Прежде всего следует заметить, что в классе MyClass определен открытый конструктор и ряд открытых методов, в том числе и перегружаемый метод Set (). Объект класса Туре, представляющий класс MyClass, создается в методе Main () в следующей строке кода. Type t = typeof(MyClass); // получить объект класса Туре, // представляющий класс MyClass Напомним, что оператор typeof возвращает объект класса Туре, представляющий конкретный тип данных (в данном случае – класс MyClass). С помощью переменной t и прикладного интерфейса Reflection API в данной программе затем выводятся сведения о методах, поддерживаемых в классе MyClass. Для этого в приведенной ниже строке кода сначала выводится список соответствующих методов. MethodInfo[] mi = t.GetMethods(); Затем в цикле foreach организуется обращение к элементам массива mi. На каждом шаге этого цикла выводится возвращаемый тип, имя и параметры отдельного метода, как показано в приведенном ниже фрагменте кода. foreach(Methodlnfo m in mi) { // Вывести возвращаемый тип и имя каждого метода. Console.Write(" " + m.ReturnType.Name + " " + m.Name + "("); // Вывести параметры. Parameterlnfo[] 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 происходит обращение к элементам массива pin выводится тип и имя каждого параметра. Самое главное, что все эти сведения создаются динамически во время выполнения программы, не опираясь на предварительную осведомленность о классе MyClass. Вторая форма метода GetMethods () Существует вторая форма метода GetMethods () , позволяющая указывать различные флажки для отфильтровывания извлекаемых сведений о методах. Ниже приведена эта общая форма метода GetMethods (). Methodlnfo[] GetMethods(BindingFlags флажки) В этом варианте создаются только те методы, которые соответствуют указанным критериям. BindingFlags представляет собой перечисление. Ниже перечислен ряд наиболее часто используемых его значений. Значение Описание DeclaredOnly Извлекаются только те методы, которые определены в заданном классе. Унаследованные методы в извлекаемые сведения не включаются Instance Извлекаются методы экземпляра NonPublic Извлекаются методы, не являющиеся открытыми Public Извлекаются открытые методы Static Извлекаются статические методы Два или несколько флажков можно объединить с помощью логической операции ИЛИ. Но как минимум флажок Instance или Static следует указывать вместе с флажком Public или Non Pub lie. В противном случае не будут извлечены сведения ни об одном из методов. Форма BindingFlags метода GetMethods () чаще всего применяется для получения списка методов, определенных в классе, без дополнительного извлечения наследуемых методов. Это особенно удобно в тех случаях, когда требуется исключить получение сведений о методах, определяемых в классе конкретного объекта. В качестве примера попробуем выполнить следующую замену в вызове метода GetMethods () из предыдущей программы. // Теперь получаются сведения только о тех методах, // которые объявлены в классе MyClass. Methodlnfo[] 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 () , входящий в состав класса Methodlnf о. Ниже приведена одна из форм этого метода: object Invoke(object obj, object[] parameters) где obj обозначает ссылку на объект, для которого вызывается метод. Для вызова статических методов (static) в качестве параметра obj передается пустое значение (null). Любые аргументы, которые должны быть переданы методу, указываются в массиве parameters. Если же аргументы не нужны, то вместо массива parameters указывается пустое значение (null). Кроме того, количество элементов массива parameters должно точно соответствовать количеству передаваемых аргументов. Так, если требуется передать два аргумента, то массив parameters должен состоять из двух элементов, но не из трех или четырех. Значение, возвращаемое вызываемым методом, передается методу Invoke () , который и возвращает его. Для вызова конкретного метода достаточно вызвать метод Invoke () для экземпляра объекта типа Methodlnf о, получаемого при вызове метода GetMethods () . Эта процедура демонстрируется в приведенном ниже примере программы. // Вызвать методы с помощью рефлексии. using System; using System.Reflection; class MyClass { int x; int y; public MyClass(int i, int j) { x = i; У = 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; У = b; Show(); } // Перегрузить метод Set. public void Set(double a, double b) { Console.Write("В методе Set(double, double). "); x = (int) a; у = (int) b; Show () ; } public void Show() { Console.WriteLine("Значение x: {0}, значение у: {1}", x, у); } } class InvokeMethDemo { 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(); // Вызвать каждый метод, foreach(Methodlnfo m in mi) { // Получить параметры. Parameterlnfo[] pi = m.GetParameters() ; if(m.Name.CompareTo("Set")==0 && pi[0].ParameterType == typeof(int)) { object[] args = new object[2]; args[0] = 9; args[l] = 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("Сумма равна " + 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 и у"); } else if(m.Name.CompareTo("Show")==0) { m.Invoke(reflectOb, null); } } } } Вот к какому результату приводит выполнение этой программы. Вызов методов, определенных в классе MyClass Сумма равна 30 Значение 14 находится между х и у В методе Set (int, int). Значение х: 9, значение у: 18 В методе Set(double, double). Значение х: 1, значение у: 23 Значение х: 1, значение у: 23 Рассмотрим подробнее порядок вызова методов. Сначала создается список методов. Затем в цикле foreach извлекаются сведения об их параметрах. Далее каждый метод вызывается с указанием соответствующего типа и числа аргументов в последовательном ряде условных операторов if/else. Обратите особое внимание на перегрузку метода Set () в приведенном ниже фрагменте кода. if(m.Name.CompareTo("Set")==0 && pi(0].ParameterType == typeof(int)) { object[] args = new object[2]; args[0] = 9; args[l] = 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); } Если имя метода – Set, то проверяется тип первого параметра, чтобы выявить конкретный вариант этого метода. Так, если это метод Set (int, int), то его аргументы загружаются в массив args. В противном случае используются аргументы типа double. Получение конструкторов конкретного типа В предыдущем примере при вызове методов, определенных в классе MyClass, преимущества рефлексии не использовались, поскольку объект типа MyClass создавался явным образом. В таком случае было бы намного проще вызвать для него методы обычным образом. Но сильные стороны рефлексии проявляются наиболее заметно лишь в том случае, если объект создается динамически во время выполнения. И для этого необходимо получить сначала список конструкторов, а затем экземпляр объекта заданного типа, вызвав один из этих конструкторов. Такой механизм позволяет получать во время выполнения экземпляр объекта любого типа, даже не указывая его имя в операторе объявления. Конструкторы конкретного типа получаются при вызове метода GetConstructors () для объекта класса Туре. Ниже приведена одна из наиболее часто используемых форм этого метода. Constructorlnfo[] GetConstructors() Метод GetConstructors () возвращает массив объектов класса Constructorlnfo, описывающих конструкторы. Класс Constructorlnfo является производным от абстрактного класса MethodBase, который в свою очередь наследует от класса Memberlnf о. В нем также определен ряд собственных методов. К их числу относится интересующий нас метод GetConstructors (), возвращающий список параметров, связанных с конструктором. Этот метод действует таким же образом, как и упоминавшийся ранее метод GetParameters () , определенный в классе Methodlnf о. Как только будет обнаружен подходящий конструктор, для создания объекта вызывается метод Invoke () , определенный в классе Constructorlnfo. Ниже приведена одна из форм этого метода. object Invoke(object[] parameters ) Любые аргументы, которые требуется передать методу, указываются в массиве parameters. Если же аргументы не нужны, то вместо массива parameters указывается пустое значение (null). Но в любом случае количество элементов массива parameters должно совпадать с количеством передаваемых аргументов, а типы аргументов – с типами параметров. Метод Invoke () возвращает ссылку на сконструированный объект. В приведенном ниже примере программы рефлексия используется для создания экземпляра объекта класса MyClass. // Создать объект с помощью рефлексии. using System; using System.Reflection; class MyClass { int x; int y; l public MyClass(int i) { Console.WriteLine("Конструирование класса MyClass(int, int). "); x = у = i; } public MyClass(int i, int j) { Console.WriteLine("Конструирование класса MyClass(int, int). "); x = i; У = 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; У = b; Show () ; } // Перегрузить метод Set. public void Set(double a, double b) { Console.Write("В методе(double, double). "); ■ x = (int) a; у = (int) b; Show(); } public void Show() { Console.WriteLine("Значение x: {0}, значение у: {1}", x, у); } } class InvokeConsDemo { static void Main() { Type t = typeof(MyClass); int val; // Получить сведения о конструкторе. Constructorlnfo[] ci = t.GetConstructors(); Console.WriteLine("Доступные конструкторы: "); foreach(Constructorlnfo с in ci) { // Вывести возвращаемый тип и имя. Console.Write(" " + t.Name + "("); // Вывести параметры. Parameterlnfo[] pi = с.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(", "); |