Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка ПИ Программирование на С# _Хотов.docx
Скачиваний:
11
Добавлен:
01.07.2025
Размер:
2 Мб
Скачать
☆
        1. Поиск реализованных интерфейсов

Чтобы получить все реализованные типом интерфейсы, надо использовать метод GetInterfaces(), который возвращает массив объектов Type:

Console.WriteLine("Реализованные интерфейсы:");

foreach (Type i in myType.GetInterfaces())

{

Console.WriteLine(i.Name);

}

Так как каждый интерфейс представляет объект Type, то для каждого полученного интерфейса можно также применить выше рассмотренные методы для извлечения информации о свойствах и методах

Объединив все выше рассмотренные методы, можно провести комплексное изучение определенного типа и получить все его составные части.

      1. Динамическая загрузка сборок и позднее связывание

При создании приложения мы уже определяем набор сборок, которые будут приложением использоваться. Однако может потребоваться динамически загружать другие сборки.

Для управления сборками в пространстве имен System.Reflection имеется класс Assembly. С его помощью можно загружать сборку, исследовать ее.

Чтобы динамически загрузить сборку в приложение, надо использовать статические методы Assembly.LoadFrom() илиAssembly.Load().

Метод LoadFrom() принимает в качестве параметра путь к сборке. Например, исследуем сборку на наличие в ней различных типов:

static void Main(string[] args)

{

Assembly asm = Assembly.LoadFrom("TestConsole.exe");

Console.WriteLine(asm.FullName);

// получаем все типы из сборки TestConsole.exe

Type[] types = asm.GetTypes();

foreach(Type t in types)

{

Console.WriteLine(t.Name);

}

Console.ReadLine();

}

В данном случае для исследования указывается сборка TestConsole.exe. Здесь использован относительный путь, так как сборка находится в одной папке с приложением - в проекте в каталоге bin/Debug. Можно в принципе в качестве имени указать и имя текущего приложение. В этом случае программа будет исследовать саму себя.

Метод Load() действует аналогично, только в качестве его параметра передается дружественное имя сборки, которое нередко совпадает с именем приложения: Assembly asm = Assembly.Load("TestConsole");

Получив все типы сборки с помощью метода GetTypes(), мы опять же можем применить к каждому типу все те методы, которые были рассмотрены в прошлой теме.

        1. Позднее связывание

С помощью динамической загрузки мы можем реализовать технологию позднего связывания. Позднее связывание позволяет создавать экземпляры некоторого типа, а также использовать его во время выполнения приложения.

Использование позднего связывания менее безопасно в том плане, что при жестком кодировании всех типов (ранее связывание) на этапе компиляции мы можем отследить многие ошибки. В то же время позднее связывание позволяет создавать расширяемые приложения, когда дополнительный функционал программы неизвестен, и его могут разработать и подключить сторонние разработчики.

Ключевую роль в позднем связывании играет класс System.Activator. С помощью его статического методаActivator.CreateInstance() можно создавать экземпляры заданного типа.

Например, динамически загрузим сборку и вызовем у ней некоторый метод. Допустим, загружаемая сборка MyApp.exe представляет следующую программу:

class Program

{

static void Main(string[] args)

{

Console.WriteLine(GetResult(6, 100, 2));

Console.ReadLine();

}

public static double GetResult(int percent, double capital, int year)

{

for (int i = 0; i < year; i++ )

{

capital += capital /100 * percent;

}

return capital;

}

}

Итак, у нас стандартный класс Program с двумя методами. Теперь динамически подключим сборку с этой программой в другой программе и вызовем ее методы.

Пусть наша основная программа будет выглядеть так:

static void Main(string[] args)

{

try

{

Assembly asm = Assembly.LoadFrom("MyApp.exe");

Type t = asm.GetType("MyApp.Program", true, true);

// создаем экземпляр класса Program

object obj = Activator.CreateInstance(t);

// получаем метод GetResult

MethodInfo method = t.GetMethod("GetResult");

// вызываем метод, передаем ему значения для параметров и получаем результат

object result = method.Invoke(obj, new object[] { 6, 100, 3 });

Console.WriteLine((result));

}

catch(Exception ex)

{

Console.WriteLine(ex.Message);

}

Console.ReadLine();

}

Предположим, что исследуемая сборка находится в проекте в каталоге bin/Debug, тогда ее можно получить с помощью выражения Assembly asm = Assembly.LoadFrom("MyApp.exe").

Затем с помощью метода GetType получаем тип - класс Program, который находится в сборке MyApp.exe: Type t = asm.GetType("MyApp.Program", true, true);

Получив тип, создаем его экземпляр: object obj = Activator.CreateInstance(t). Результат создания - объект класса Program представляет собой переменную obj.

И в конце остается вызвать метод. Во-первых, получаем сам метод: MethodInfo method = t.GetMethod("GetResult");. И потом с помощью метода Invoke вызываем его: object result = method.Invoke(obj, new object[] { 6, 100, 3 }). Здесь первый параметр представляет объект, для которого вызывается метод, а второй - набор параметров в виде массива object[].

Так как метод GetResult возвращает некоторое значение, то мы можем его получить из метода в виде объекта типа object.

Если бы метод не принимал параметров, то вместо массива объектов использовалось бы значение null: method.Invoke(obj, null)

В сборке MyApp.exe в классе Program также есть и другой метод - метод Main. Вызовем теперь его:

Console.WriteLine("Вызов метода Main");

method = t.GetMethod("Main", BindingFlags.DeclaredOnly

| BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);

method.Invoke(obj, new object[]{new string[]{}});

Так как метод Main является статическим и не публичным, то к нему используется соответствующая битовая маскаBindingFlags.NonPublic | BindingFlags.Static. И поскольку он в качестве параметра принимает массив строк, то при вызове метода передается соответствующее значение: method.Invoke(obj, new object[]{new string[]{}})