Эта программа дает следующий результат. Доступные конструкторы: 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. Сначала получается перечень открытых конструкторов в следую щей строке кода. ConstructorInfо[] 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
Этот метод возвращает массив типов, содержащихся в сборке.