Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Отражение позволяет проверять известные типы данных во время выполнения. Отражение позволяет перечислить типы данных в данной сборке, а элементы заданного класса или типа значений можно обнаружить. Это верно независимо от того, был ли тип известен или ссылался во время компиляции. Это делает отражение полезной функцией для средств разработки и управления кодом.
Обратите внимание, что указанное имя сборки — это строгое имя (см. статью "Создание и использование сборок с строгим именем"), включающее версию сборки, язык и региональные параметры и сведения о подписи. Обратите внимание также, что имя пространства имен, в котором определен тип данных, можно получить вместе с именем базового класса.
Наиболее распространенным способом доступа к функциям отражения является GetType метод. Этот метод предоставляется методом System.Object, из которого наследуются все собранные мусором классы.
Примечание.
Отражение .exe, созданного с помощью компилятора Microsoft C++, допускается только в том случае, если .exe построен с использованием параметров компиляции /clr:pure или /clr:safe. Параметры компилятора /clr:pure и /clr:safe компилятора устарели в Visual Studio 2015 и недоступны в Visual Studio 2017. Дополнительные сведения см. в разделе /clr (компиляция среды CLR).
Дополнительные сведения см. в разделе System.Reflection.
Пример: GetType
Метод GetType возвращает указатель на объект класса Type, который описывает тип, на котором основан объект. (Объект Type не содержит сведений, относящихся к конкретному экземпляру.) Одним из таких элементов является полное имя типа, которое можно вывести следующим образом:
Обратите внимание, что имя типа включает полную область, в которой определен тип, включая пространство имен, и что он отображается в синтаксисе .NET, с точкой в качестве оператора разрешения области.
// vcpp_reflection.cpp
// compile with: /clr
using namespace System;
int main() {
String ^ s = "sample string";
Console::WriteLine("full type name of '{0}' is '{1}'", s, s->GetType());
}
full type name of 'sample string' is 'System.String'
Пример: упакованные типы значений
Типы значений также можно использовать с GetType функцией, но сначала их необходимо упаковать.
// vcpp_reflection_2.cpp
// compile with: /clr
using namespace System;
int main() {
Int32 i = 100;
Object ^ o = i;
Console::WriteLine("type of i = '{0}'", o->GetType());
}
type of i = 'System.Int32'
Пример: typeid
Как и в GetType случае с методом, оператор typeid возвращает указатель на объект Type , поэтому этот код указывает имя типа System.Int32. Отображение имен типов является наиболее базовой функцией отражения, но потенциально более полезный способ заключается в проверке или обнаружении допустимых значений для перечисленных типов. Это можно сделать с помощью статической функции Enum::GetNames , которая возвращает массив строк, каждый из которых содержит значение перечисления в текстовой форме. В следующем примере извлекается массив строк, описывающий значения перечисления Options (CLR) и отображающий их в цикле.
Если четвертый параметр добавляется в перечисление "Параметры ", этот код сообщает о новом параметре без повторной компиляции, даже если перечисление определено в отдельной сборке.
// vcpp_reflection_3.cpp
// compile with: /clr
using namespace System;
enum class Options { // not a native enum
Option1, Option2, Option3
};
int main() {
array<String^>^ names = Enum::GetNames(Options::typeid);
Console::WriteLine("there are {0} options in enum '{1}'",
names->Length, Options::typeid);
for (int i = 0 ; i < names->Length ; i++)
Console::WriteLine("{0}: {1}", i, names[i]);
Options o = Options::Option2;
Console::WriteLine("value of 'o' is {0}", o);
}
there are 3 options in enum 'Options'
0: Option1
1: Option2
2: Option3
value of 'o' is Option2
Пример: элементы и свойства GetType
Объект GetType поддерживает ряд элементов и свойств, которые можно использовать для проверки типа. Этот код извлекает и отображает некоторые из этих сведений:
// vcpp_reflection_4.cpp
// compile with: /clr
using namespace System;
int main() {
Console::WriteLine("type information for 'String':");
Type ^ t = String::typeid;
String ^ assemblyName = t->Assembly->FullName;
Console::WriteLine("assembly name: {0}", assemblyName);
String ^ nameSpace = t->Namespace;
Console::WriteLine("namespace: {0}", nameSpace);
String ^ baseType = t->BaseType->FullName;
Console::WriteLine("base type: {0}", baseType);
bool isArray = t->IsArray;
Console::WriteLine("is array: {0}", isArray);
bool isClass = t->IsClass;
Console::WriteLine("is class: {0}", isClass);
}
type information for 'String':
assembly name: mscorlib, Version=1.0.5000.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
namespace: System
base type: System.Object
is array: False
is class: True
Пример: перечисление типов
Отражение также позволяет перечислять типы в сборке, а также участников в классах. Чтобы продемонстрировать эту функцию, определите простой класс:
// vcpp_reflection_5.cpp
// compile with: /clr /LD
using namespace System;
public ref class TestClass {
int m_i;
public:
TestClass() {}
void SimpleTestMember1() {}
String ^ SimpleMember2(String ^ s) { return s; }
int TestMember(int i) { return i; }
property int Member {
int get() { return m_i; }
void set(int i) { m_i = i; }
}
};
Пример: проверка сборок
Если приведенный выше код компилируется в библиотеку DLL с именем vcpp_reflection_6.dll, можно использовать отражение для проверки содержимого этой сборки. Это включает использование функции API статической рефлексии xref:System.Reflection.Assembly.Load%2A?displayProperty=nameWithType для загрузки сборки. Эта функция возвращает адрес объекта сборки, который затем можно запрашивать о модулях и типах внутри.
После того как система отражений успешно загружает сборку, массив объектов Type извлекается с помощью функции Assembly.GetTypes. Каждый элемент массива содержит сведения о другом типе, хотя в этом случае определяется только один класс. Используя цикл, каждый тип в этом массиве запрашивается о его членах типа с помощью функции Type::GetMembers. Эта функция возвращает массив объектов MethodInfo , каждый объект, содержащий сведения о функции-члене, члене данных или свойстве типа.
Обратите внимание, что список методов включает функции, явно определенные в TestClass , и функции, неявно унаследованные от класса System::Object . В рамках описания в .NET, а не в синтаксисе Visual C++ свойства отображаются как базовый элемент данных, к которым обращается функция get/set. Функции get/set отображаются в этом списке как обычные методы. Отражение поддерживается с помощью общей среды выполнения Microsoft CLR, а не компилятора Microsoft C++.
Хотя этот код использовался для проверки определенной сборки, вы также можете использовать этот код для проверки сборок .NET. Например, если изменить TestAssembly на mscorlib, вы увидите список всех типов и методов, определенных в mscorlib.dll.
// vcpp_reflection_6.cpp
// compile with: /clr
using namespace System;
using namespace System::IO;
using namespace System::Reflection;
int main() {
Assembly ^ a = nullptr;
try {
// load assembly -- do not use file extension
// will look for .dll extension first
// then .exe with the filename
a = Assembly::Load("vcpp_reflection_5");
}
catch (FileNotFoundException ^ e) {
Console::WriteLine(e->Message);
return -1;
}
Console::WriteLine("assembly info:");
Console::WriteLine(a->FullName);
array<Type^>^ typeArray = a->GetTypes();
Console::WriteLine("type info ({0} types):", typeArray->Length);
int totalTypes = 0;
int totalMembers = 0;
for (int i = 0 ; i < typeArray->Length ; i++) {
// retrieve array of member descriptions
array<MemberInfo^>^ member = typeArray[i]->GetMembers();
Console::WriteLine(" members of {0} ({1} members):",
typeArray[i]->FullName, member->Length);
for (int j = 0 ; j < member->Length ; j++) {
Console::Write(" ({0})",
member[j]->MemberType.ToString() );
Console::Write("{0} ", member[j]);
Console::WriteLine("");
totalMembers++;
}
totalTypes++;
}
Console::WriteLine("{0} total types, {1} total members",
totalTypes, totalMembers);
}
Руководство по реализации архитектуры встраиваемого компонента с использованием отражения.
В следующих примерах кода демонстрируется использование отражения для реализации простой архитектуры подключаемых модулей. Первый список — это приложение, а второй — подключаемый модуль. Приложение представляет собой форму с несколькими документами, которая заполняется с использованием любых классов, основанных на формах, найденных в DLL подключаемого модуля, предоставляемого в качестве аргумента командной строки.
Приложение пытается загрузить предоставленную сборку с помощью System.Reflection.Assembly.Load метода. В случае успешного выполнения типы внутри сборки перечисляются с помощью метода System.Reflection.Assembly.GetTypes. Затем каждый тип проверяется на совместимость с помощью System.Type.IsAssignableFrom метода. В этом примере классы, найденные в предоставленной сборке, должны быть производными от Form класса, чтобы квалифицироваться как подключаемый модуль.
Совместимые классы затем создаются с помощью метода System.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);
}
}
};
Как: Перечисление типов данных в сборках с помощью рефлексии
В следующем коде показано перечисление общедоступных типов и членов с помощью System.Reflection.
Учитывая имя сборки, находящейся либо в локальной директории, либо в GAC, приведенный ниже код пытается открыть эту сборку и получить её описания. При успешном выполнении каждый тип отображается со своими общедоступными элементами.
Обратите внимание, что требуется, чтобы System.Reflection.Assembly.Load расширение файла не использовалось. Следовательно, использование "mscorlib.dll" в качестве аргумента командной строки завершится ошибкой, тогда как использование только "mscorlib" приведет к отображению типов .NET Framework. Если имя сборки не указано, код обнаружит и сообщит о типах в текущей сборке (EXE, полученный из этого кода).
Пример
// self_reflection.cpp
// compile with: /clr
using namespace System;
using namespace System::Reflection;
using namespace System::Collections;
public ref class ExampleType {
public:
ExampleType() {}
void Func() {}
};
int main() {
String^ delimStr = " ";
array<Char>^ delimiter = delimStr->ToCharArray( );
array<String^>^ args = Environment::CommandLine->Split( delimiter );
// replace "self_reflection.exe" with an assembly from either the local
// directory or the GAC
Assembly^ a = Assembly::LoadFrom("self_reflection.exe");
Console::WriteLine(a);
int count = 0;
array<Type^>^ types = a->GetTypes();
IEnumerator^ typeIter = types->GetEnumerator();
while ( typeIter->MoveNext() ) {
Type^ t = dynamic_cast<Type^>(typeIter->Current);
Console::WriteLine(" {0}", t->ToString());
array<MemberInfo^>^ members = t->GetMembers();
IEnumerator^ memberIter = members->GetEnumerator();
while ( memberIter->MoveNext() ) {
MemberInfo^ mi = dynamic_cast<MemberInfo^>(memberIter->Current);
Console::Write(" {0}", mi->ToString( ) );
if (mi->MemberType == MemberTypes::Constructor)
Console::Write(" (constructor)");
Console::WriteLine();
}
count++;
}
Console::WriteLine("{0} types found", count);
}