Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Reflektion gör att kända datatyper kan inspekteras vid körtid. Reflektion gör det möjligt att räkna upp datatyper i en viss sammansättning, och medlemmar i en viss klass eller värdetyp kan identifieras. Detta gäller oavsett om typen var känd eller refererad vid kompileringstillfället. Detta gör reflektion till en användbar funktion för utveckling och kodhanteringsverktyg.
Observera att det angivna sammansättningsnamnet är det starka namnet (se Skapa och använda Strong-Named-sammansättningar), vilket inkluderar versionsinformation, kultur och signeringsinformation. Observera också att namnet på namnområdet där datatypen definieras kan hämtas, tillsammans med namnet på basklassen.
Det vanligaste sättet att komma åt reflektionsfunktioner är genom GetType metoden. Den här metoden tillhandahålls av System.Object, från vilken alla skräpinsamlingsklasser härleds.
Anmärkning
Reflektion över en .exe som skapats med Microsoft C++-kompilatorn tillåts endast om .exe skapas med kompilatoralternativen /clr:pure eller /clr:safe . Alternativen /clr:pure och /clr:safe är inaktuella i Visual Studio 2015 och är inte tillgängliga i Visual Studio 2017. Mer information finns i /clr (Common Language Runtime Compilation).
För mer information, se System.Reflection
Exempel: GetType
Metoden GetType returnerar en pekare till ett objekt av klassen Type, vilket beskriver typen på vilken objektet baseras. ( Typobjektet innehåller ingen instansspecifik information.) Ett sådant objekt är det fullständiga namnet på typen, som kan visas på följande sätt:
Observera att typnamnet innehåller det fullständiga omfång där typen definieras, inklusive namnområdet, och att den visas i .NET-syntaxen, med en punkt som omfångsmatchningsoperator.
// 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'
Exempel: boxade värdetyper
Värdetyper kan också användas med funktionen GetType, men de måste vara boxade först.
// 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'
Exempel: typeid
Precis som GetType med metoden returnerar typeid-operatorn en pekare till ett Typ-objekt , så den här koden anger typnamnet System.Int32. Att visa typnamn är den mest grundläggande funktionen för reflektion, men en potentiellt mer användbar teknik är att inspektera eller identifiera giltiga värden för uppräknade typer. Detta kan göras med hjälp av den statiska funktionen Enum::GetNames , som returnerar en matris med strängar som var och en innehåller ett uppräkningsvärde i textformat. Följande exempel hämtar ett fält med strängar som beskriver uppräkningsvärdena för Alternativ-uppräkningen (CLR) och visar dem i en loop.
Om ett fjärde alternativ läggs till i uppräkningen Alternativ rapporterar den här koden det nya alternativet utan omkompilering, även om uppräkningen definieras i en separat sammansättning.
// 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
Exempel: GetType-medlemmar och egenskaper
Objektet GetType stöder ett antal medlemmar och egenskaper som kan användas för att undersöka en typ. Den här koden hämtar och visar en del av den här informationen:
// 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
Exempel: uppräkning av typer
Reflektion möjliggör också uppräkning av typer inom en sammansättning och medlemmar i klasser. Om du vill demonstrera den här funktionen definierar du en enkel klass:
// 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; }
}
};
Exempel: inspektion av sammansättningar
Om koden ovan kompileras till en DLL med namnet vcpp_reflection_6.dllkan du använda reflektion för att granska innehållet i den här sammansättningen. Detta innebär att använda funktionen static reflection API xref:System.Reflection.Assembly.Load%2A?displayProperty=nameWithType för att läsa in sammansättningen. Den här funktionen returnerar adressen till ett sammansättningsobjekt som sedan kan frågas om modulerna och typerna i.
När reflektionssystemet har läst in sammansättningen hämtas en matris med typobjekt med Assembly.GetTypes funktionen. Varje matriselement innehåller information om en annan typ, men i det här fallet definieras endast en klass. Med hjälp av en loop frågas varje typ i den här matrisen om typen medlemmar med hjälp av funktionen Type::GetMembers . Den här funktionen returnerar en matris med MethodInfo-objekt , varje objekt som innehåller information om medlemsfunktionen, datamedlemmen eller egenskapen i typen.
Observera att listan över metoder innehåller de funktioner som uttryckligen definierats i TestClass och de funktioner som implicit ärvts från klassen System::Object . Som en del av beskrivningen i .NET i stället för i Visual C++-syntaxen visas egenskaper som den underliggande datamedlem som används av get/set-funktionerna. Funktionerna get/set visas i den här listan som vanliga metoder. Reflektion stöds via den vanliga språkkörningen, inte av Microsoft C++-kompilatorn.
Även om du använde den här koden för att inspektera en sammansättning som du har definierat kan du också använda den här koden för att inspektera .NET-sammansättningar. Om du till exempel ändrar TestAssembly till mscorlib visas en lista över alla typer och metoder som definierats i 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);
}
Gör så här: Implementera en Plug-In-komponentarkitektur med reflektion
Följande kodexempel visar hur reflektion används för att implementera en enkel "plugin-arkitektur". Den första listan är programmet och det andra är plugin-programmet. Programmet är ett formulär med flera dokument som fyller sig med hjälp av alla formulärbaserade klasser som finns i plugin-DLL:en som tillhandahålls som ett kommandoradsargument.
Programmet försöker läsa in den angivna sammansättningen med hjälp av System.Reflection.Assembly.Load metoden . Om det lyckas räknas typerna i sammansättningen upp med hjälp av System.Reflection.Assembly.GetTypes metoden . Varje typ kontrolleras sedan för kompatibilitet med hjälp av System.Type.IsAssignableFrom metoden. I det här exemplet måste klasser som finns i den angivna sammansättningen härledas från Form klassen för att kvalificeras som ett plugin-program.
Kompatibla klasser instansieras sedan med System.Activator.CreateInstance metoden, som accepterar ett Type som argument och returnerar en pekare till en ny instans. Varje ny instans kopplas sedan till formuläret och visas.
Observera att Load metoden inte accepterar sammansättningsnamn som innehåller filnamnstillägget. Huvudfunktionen i programmet trimmar eventuella angivna tillägg, så följande kodexempel fungerar i båda fallen.
Exempelapp
Följande kod definierar programmet som accepterar plugin-program. Ett sammansättningsnamn måste anges som det första argumentet. Den här sammansättningen bör innehålla minst en offentlig Form härledd typ.
// 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));
}
Exempel på plugin-program
Följande kod definierar tre klasser som härletts från Form. När namnet på det resulterande sammansättningsnamnet skickas till den körbara filen i föregående lista identifieras och instansieras var och en av dessa tre klasser, trots att de alla var okända för värdprogrammet vid kompileringen.
// 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);
}
}
};
Gör så här: Räkna upp datatyper i assembly med Reflection
Följande kod visar uppräkning av offentliga typer och medlemmar med hjälp av System.Reflection.
Med namnet på en sammansättning, antingen i den lokala katalogen eller i GAC, försöker koden nedan öppna sammansättningen och hämta beskrivningar. Om det är framgångsrikt visas varje typ med sina offentliga medlemmar.
Observera att System.Reflection.Assembly.Load kräver att inget filnamnstillägg används. Därför misslyckas användningen av "mscorlib.dll" som ett kommandoradsargument, medan användning av bara "mscorlib" resulterar i att .NET Framework-typerna visas. Om inget sammansättningsnamn anges identifierar och rapporterar koden typerna i den aktuella sammansättningen (EXE som är resultatet av den här koden).
Exempel
// 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);
}