Динамическое связывание
Отражение может также использоваться для реализации динамического связывания. Динамическое связывание состоит в том, что метод, который нужно вызвать, определяется в процессе выполнения, а не на этапе компиляции. Это один из примеров того, как метаданные используются для предоставления функциональных возможностей. Предыдущий пример демонстрирует, как получить сигнатуру метода, связанного с типом. Объект Methodlnfо содержит все необходимые метаданные для метода класса. Пример Dynamic (Динамический) демонстрирует очень простой случай динамического связывания.
Мы динамически загружаем сборку и получаем метаданные для метода определенного типа:
// Загрузить (Load) сборку Customer (Клиент) Assembly *a = Assembly::Load("Customer"); // Загрузка ("Клиент") // Получить метаданных для класса Customers (Клиенты) // и одного метода Type *t = a › GetType("01. NetCpp.Acme.Customers"); // Клиенты Methodlnfо *mi = t › GetMethod("GetCustomer");
Вот что следует помнить программистам при программировании на C++. Работая со строками, которые содержат пространства имен или классы, необходимо использовать должным образом отформатированные строки, которые понятны методам класса отражения. Например, полностью определенное имя класса в вышеописанном коде – OI. NetCpp.Acme.Customers, а не OI::NetCpp::Acme::Customers, отформатированное в стиле C++. Таким образом, используемый формат подобен формату в С#, а не в C++.
Применяя классы отражения, мы могли бы сделать все это полностью динамически, произвольно выбирая типы, методы, и конструкторы из сборки Customer (Клиент), используя методы последнего примера, но мы хотели сохранить пример Dynamic (Динамический) простым. Более честолюбивая программа могла бы делать что-нибудь гораздо более интересное, вроде реализации декомпилятора сборки, который непосредственно из откомпилированной сборки генерирует исходный текст на управляемом C++, С#илиУВ.НЕТ.
Пространство имен System (Система) содержит класс Activator (Активатор, Модуль активизации), в котором перегружается метод Createlnstance, предназначенный для создания экземпляров любого типа .NET с помощью подходящего конструктора. Класс Activator (Активатор, Модуль активизации) рассматривается в этой главе в разделе, посвященном удаленному доступу. Чтобы создать экземпляр объекта Customers (Клиенты), мы вызываем конструктор без параметров.
Type *t = a › GetType("01. NetCpp.Acme.Customers"); // Клиенты Object *customerlnstance = // Объект Activator::Createlnstance(t); // Активатор
Затем, чтобы вызвать метод GetCustomer, формируем список параметров и используем метод Invoke (Вызвать) экземпляра Methodlnfо. Dynamic\Customer\Debug в папку Dynamic\Debug перед выполнение Dynamic.ехе.
// вызвать метод Object *arguments [] = new Object*[1]; // новый Объект int customerld = -1; arguments[0] = _box(customerld); // параметры Object *returnType = mi › Invoke(// Вызвать customerlnstance, arguments); // параметры
Используя методы отражения, мы получаем информацию о типе для каждого поля в возвращаемой структуре. Обратите внимание, что метод GetValue, принадлежащий Fieldlnfо, возвращает данные для конкретного поля в объекте.
if (returnType › GetType() == Type::GetType("System.Collections.ArrayList")) // ("Система.Коллекции.Список массивов") { ArrayList *arrayList = dynamic_cast<ArrayList *>(returnType); for (int i = 0; i<arrayList › Count; i++) // Счет { Type *itemType = arrayList › get_Item(i) › GetType(); Fieldlnfo *fi [] = itemType › GetFields(); for (int j = 0; j < fi › Length; j++) { Object *fieldValue = // Объект fi[j] › GetValue(arrayList › get_Item(i)); Console::Write(// Запись "{0, -10} = {1, -15}", fi[j] › Name, fieldValue); // Имя } Console::WriteLine(); } }
Снова обращаем внимание на то, что в строке System.Collections.ArrayList (Система.Коллекции.Список массивов) для отделения имен использованы точки, а не "двойные двоеточия.
В этом коде не использованы никакие определенные объекты или типы из сборки Customer (Клиент). С целью проиллюстрировать главные принципы, мы применили некоторые знания о сборке, чтобы код был простым. Однако должно быть понятно, как сделать его полностью общим.
Можно сделать шаг вперед и использовать классы, которые генерируют метаданные (в System::Reflection::Emit (Система-Отражение-Генерация)). Можно даже динамически создавать сборку в памяти, а затем загружать и выполнять ее!