Практическое руководство. Реализация архитектуры с подключаемыми компонентами с помощью отражения (C++/CLI)
В следующих примерах кода показаны способы использования отражения для реализации простой "подключаемой" архитектуры.Первый перечень содержит список приложений, а второй — подключаемый модуль.Приложение является формой нескольких документов, которые заполняются с помощью классов на основе форм, предоставляемых подключаемой библиотекой DLL в качестве аргументов командной строки.
Предложение пытается загрузить предоставляемую сборку с помощью метода Assembly.Load.В случае успешной загрузки типы в сборке перечисляются с помощью метода Assembly.GetTypes.Затем каждый тип проверяется на предмет совместимости с помощью метода Type.IsAssignableFrom.В приведенном примере классы в предоставленной сборке, чтобы считаться подключаемыми модулями, должны являться производными от класса Form.
Затем создаются экземпляры совместимых классов с помощью метода Activator.CreateInstance, который принимает Type в качестве аргумента и возвращает указатель новому экземпляру.Каждый новый экземпляр затем прикрепляется к форме и отображается.
Обратите внимание, что метод Load не принимает имена сборок, которые содержат расширение файла.Основная функция приложения обрезает любые предоставленные расширения, поэтому в любом случае будет выполняться следующий код.
Пример
Следующий код определяет приложение, которое принимает подключаемые модули.В качестве первого аргумента необходимо предоставить имя сборки.Эта сборка должна содержать как минимум один открытый тип, производный от типа Form.
// plugin_application.cpp
// compile with: /clr /c
#using <system.dll>
#using <system.drawing.dll>
#using <system.windows.forms.dll>
using namespace System;
using namespace System::Windows::Forms;
using namespace System::Reflection;
ref class PluggableForm : public Form {
public:
PluggableForm() {}
PluggableForm(Assembly^ plugAssembly) {
Text = "plug-in example";
Size = Drawing::Size(400, 400);
IsMdiContainer = true;
array<Type^>^ types = plugAssembly->GetTypes( );
Type^ formType = Form::typeid;
for (int i = 0 ; i < types->Length ; i++) {
if (formType->IsAssignableFrom(types[i])) {
// Create an instance given the type description.
Form^ f = dynamic_cast<Form^> (Activator::CreateInstance(types[i]));
if (f) {
f->Text = types[i]->ToString();
f->MdiParent = this;
f->Show();
}
}
}
}
};
int main() {
Assembly^ a = Assembly::LoadFrom("plugin_application.exe");
Application::Run(gcnew PluggableForm(a));
}
Следующий код определяет три класса, производные от Form.Если имя результирующей сборки будет передано исполняемому файлу из предыдущего перечня, все три класса будут обнаружены и для каждого из них будет создан экземпляр, несмотря на то что все они неизвестны ведущему приложению во время компиляции.
// plugin_assembly.cpp
// compile with: /clr /LD
#using <system.dll>
#using <system.drawing.dll>
#using <system.windows.forms.dll>
using namespace System;
using namespace System::Windows::Forms;
using namespace System::Reflection;
using namespace System::Drawing;
public ref class BlueForm : public Form {
public:
BlueForm() {
BackColor = Color::Blue;
}
};
public ref class CircleForm : public Form {
protected:
virtual void OnPaint(PaintEventArgs^ args) override {
args->Graphics->FillEllipse(Brushes::Green, ClientRectangle);
}
};
public ref class StarburstForm : public Form {
public:
StarburstForm(){
BackColor = Color::Black;
}
protected:
virtual void OnPaint(PaintEventArgs^ args) override {
Pen^ p = gcnew Pen(Color::Red, 2);
Random^ r = gcnew Random( );
Int32 w = ClientSize.Width;
Int32 h = ClientSize.Height;
for (int i=0; i<100; i++) {
float x1 = w / 2;
float y1 = h / 2;
float x2 = r->Next(w);
float y2 = r->Next(h);
args->Graphics->DrawLine(p, x1, y1, x2, y2);
}
}
};