}
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
А теперь рассмотрим порядок применения рефлексии для конструирования объекта класса MyClass
. Сначала получается перечень открытых конструкторов в следующей строке кода.
ConstructorInfo[] ci = t.GetConstructors();
Затем для наглядности примера выводятся полученные конструкторы. После этого осуществляется поиск по списку конструктора, принимающего два аргумента, как показано в приведенном ниже фрагменте кода.
for(x=0; х < ci.Length; х++) {
ParameterInfo[] pi = ci[x].GetParameters();
if(pi.Length == 2) break;
}
Если такой конструктор найден, как в данном примере, то в следующем фрагменте кода получается экземпляр объекта заданного типа.
// Сконструировать объект,
object[] consargs = new object[2];
consargs[0] = 10;
consargs[1] = 20;
object reflectOb = ci[x].Invoke(consargs);
После вызова метода Invoke()
переменная экземпляра reflectOb
будет ссылаться на объект типа MyClass
. А далее в программе выполняются соответствующие методы для экземпляра этого объекта.
Следует, однако, иметь в виду, что ради простоты в данном примере предполагается наличие лишь одного конструктора с двумя аргументами типа int
. Очевидно, что в реальном коде придется дополнительно проверять соответствие типов каждого параметра и аргумента.
В предыдущем примере все сведения о классе MyClass
были получены с помощью рефлексии, за исключением одного элемента: типа самого класса MyClass
. Несмотря на то что сведения о классе получались в предыдущем примере динамически, этот пример опирался на тот факт, что имя типа MyClass
было известно заранее и использовалось в операторе typeof
для получения объекта класса Туре
, по отношению к которому осуществлялось косвенное или непосредственное обращение к методам рефлексии. В некоторых случаях такой подход может оказаться вполне пригодным, но истинные преимущества рефлексии проявляются лишь тогда, когда доступные в программе типы данных определяются динамически в результате анализа содержимого других сборок.
Как следует из главы 16, сборка несет в себе сведения о типах классов, структур и прочих элементов данных, которые в ней содержатся. Прикладной интерфейс Reflection API позволяет загрузить сборку, извлечь сведения о ней и получить экземпляры объектов любых открыто доступных в ней типов. Используя этот механизм, программа может выявлять свою среду и использовать те функциональные возможности, которые могут оказаться доступными без явного их определения во время компиляции. Это очень эффективный и привлекательный принцип. Представьте себе, например, программу, которая выполняет роль "браузера типов", отображая типы данных, доступные в системе, или же инструментальное средство разработки, позволяющее визуально составлять программы из различных типов данных, поддерживаемых в системе. А поскольку все сведения о типах могут быть извлечены и проверены, то ограничений на применение рефлексии практически не существует.
Для получения сведений о сборке сначала необходимо создать объект класса Assembly
. В классе Assembly
открытый конструктор не определяется. Вместо этого объект класса Assembly
получается в результате вызова одного из его методов. Так, для загрузки сборки по заданному ее имени служит метод LoadFrom()
. Ниже приведена его соответствующая форма:
static Assembly LoadFrom(string файл_сборки)
где
Как только будет получен объект класса Assembly
, появится возможность обнаружить определенные в нем типы данных, вызвав для него метод GetTypes()
в приведенной ниже общей форме.
Туре [] GetTypes()