Но сборка совсем не обязательно должна быть исполняемым файлом с расширением .ехе. Сборки могут быть также в файлах динамически компонуемых библиотек (DLL) с расширением .dll. Так, если скомпилировать исходный файл MyClasses.cs в следующей командной строке:
csc /t:library MyClasses.es
то в итоге получится файл MyClasses.dll. Преимущество размещения кода в библиотеке DLL заключается, в частности, в том, что в этом случае метод Main() в исходном коде не нужен, тогда как всем исполняемым файлам требуется определенная точка входа, с которой должно начинаться выполнение программы. Именно поэтому класс Demo содержит метод Main() в качестве такой точки входа. А для библиотеки DLL метод Main() не требуется. Если же класс MyClass нужно превратить в библиотеку DLL, то в вызов метода LoadFrom() придется внести следующее изменение.
Assembly asm = Assembly.LoadFrom("MyClasses.dll");
Прежде чем завершить рассмотрение рефлексии, обратимся к еще одному поучительному примеру. Несмотря на то что в программе из предыдущего примера класс MyClass
был полноценно использован без явного указания на его имя в программе, этот пример все же опирается на предварительную осведомленность о содержимом класса MyClass
. Так, в программе были заранее известны имена методов Set
и Sum
из этого класса. Но с помощью рефлексии можно воспользоваться типом данных, ничего не зная о нем заранее. С этой целью придется извлечь все сведения, необходимые для конструирования объекта и формирования вызовов соответствующих методов. Такой подход может оказаться пригодным, например, при создании инструментального средства визуального проектирования, поскольку он позволяет использовать типы данных, имеющиеся в системе.
Рассмотрим следующий пример, демонстрирующий полностью автоматизированное обнаружение типов. В этом примере сначала загружается сборка MyClasses.ехе
, затем конструируется объект класса MyClass
и далее вызываются все методы, объявленные в классе MyClass
, причем о них ничего заранее неизвестно.
// Использовать класс MyClass, ничего не зная о нем заранее.
using System;
using System.Reflection;
class ReflectAssemblyDemo {
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();
// Использовать первый обнаруженный конструктор.
ParameterInfo[] 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);
}
else
reflectOb = 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: // один аргумент