C# 4.0 полное руководство - 2011 - Герберт Шилдт
Шрифт:
Интервал:
Закладка:
// Вызвать методы с помощью рефлексии.
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(", ");
}
Console.WriteLine(")");
}
Console.WriteLine ();
// Найти подходящий конструктор, int х;
for(x=0; х < ci.Length; х++) {
Parametgrlnfo[] pi = ci[x].GetParameters(); if(pi.Length == 2) break;
}
if (x == ci.Length) {
Console.WriteLine("Подходящий конструктор не найден."); return;
}
else
Console.WriteLine("Найден конструктор с двумя параметрами.n");
// Сконструировать объект, object[] consargs = new object[2]; consargs[0] = 10; consargs[1] = 20;
object reflectOb = ci[x].Invoke(consargs) ;
Console.WriteLine("ХпВызов методов для объекта reflectOb."); Console.WriteLine() ;
Methodlnfo[] mi = t.GetMethods();
// Вызвать каждый метод, foreach(Methodlnfo m in mi) {
// Получить параметры.
Parameterlnfo[] 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[l] = 18;
m. Invoke(reflectOb, args);
}
else if(m.Name.CompareTo("Set")==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(Int32 i)
MyClass(Int32 i, Int32 j)
Найден конструктор с двумя параметрами.
Конструирование класса MyClass(int, int)
Значение х: 10, значение у: 20
Вызов методов для объекта reflectOb
Сумма равна 30
Значение 14 находится между х и у
В методе Set(int, int). Значение х: 9, значение у: 18 В методе Set(double, double). Значение х: 1, значение у: 23 Значение х: 1, значение у: 23