Megosztás a következőn keresztül:


Reflexió (C++/CLI)

A tükröződés lehetővé teszi az ismert adattípusok futásidőben történő vizsgálatát. A tükrözés lehetővé teszi az adattípusok számbavételét egy adott szerelvényben, és egy adott osztály vagy értéktípus tagjai felderíthetők. Ez attól függetlenül igaz, hogy a típust a fordításkor ismerték vagy hivatkozták-e. A reflektálás hasznos funkció a fejlesztés és a kódkezelő eszközök számára.

Vegye figyelembe, hogy a megadott szerelvénynév az erős névvel rendelkezik (lásd: Strong-Named szerelvények létrehozása és használata), amely tartalmazza a szerelvény verzióját, a kulturális beállításokat és az aláírási információkat. Vegye figyelembe, hogy annak a névtérnek a neve, amelyben az adattípus definiálva van, lekérhető az alaposztály nevével együtt.

A tükröződési funkciók elérésének leggyakoribb módja a GetType módszer. Ezt a módszert az adja meg System.Object, amelyből az összes szemétgyűjtési osztály származik.

Megjegyzés:

A Microsoft C++ fordítóval készült .exe-on csak akkor engedélyezett a Reflection használata, ha a .exe a /clr:pure vagy /clr:safe fordító beállításaival készült. A /clr:pure és /clr:safe fordítóbeállítások elavultak a Visual Studio 2015-ben, és nem érhetők el a Visual Studio 2017-ben. További információt a /clr (Common Language Runtime Compilation) című témakörben talál.

További információért, lásd: System.Reflection

Példa: GetType

A GetType metódus egy osztályobjektumra Type mutató mutatót ad vissza, amely az objektum alapjául szolgáló típust írja le. (A Type objektum nem tartalmaz példányspecifikus információkat.) Az egyik ilyen elem a típus teljes neve, amely a következőképpen jeleníthető meg:

Vegye figyelembe, hogy a típusnév tartalmazza a teljes hatókört, amelyben a típus definiálva van, beleértve a névteret is, és hogy a .NET szintaxisban jelenik meg, a hatókörfeloldási operátor pedig ponttal.

// 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'

Példa: dobozos értéktípusok

Az értéktípusok a GetType függvényhez is használhatók, de először be kell őket jelölni.

// 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'

Példa: typeid

A metódushoz hasonlóan a GetTypetypeid operátor egy Type objektumra mutató mutatót ad vissza, így ez a kód a System.Int32 típusnevet jelzi. A típusnevek megjelenítése a tükröződés legalapvetőbb funkciója, de egy potenciálisan hasznosabb módszer az enumerált típusok érvényes értékeinek vizsgálata vagy felderítése. Ez a statikus Enum::GetNames függvénnyel végezhető el, amely egy sztringtömböt ad vissza, amelyek mindegyike szöveges formában tartalmaz enumerálási értéket. Az alábbi minta egy sztringtömböt kér le, amely a Beállítások (CLR) enumerálási értékeit írja le, és egy hurokban jeleníti meg őket.

Ha a Beállítások enumeráláshoz hozzáad egy negyedik lehetőséget, ez a kód újrafordítás nélkül jelenti az új beállítást, még akkor is, ha az enumerálás egy külön szerelvényben van definiálva.

// 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

Példa: GetType-tagok és tulajdonságok

Az GetType objektum számos tagot és tulajdonságot támogat, amelyek egy típus vizsgálatára használhatók. Ez a kód a következő információk némelyikét kéri le és jeleníti meg:

// 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

Példa: típusok számbavétele

A reflexió lehetővé teszi a típusok felsorolását egy egységen belül, valamint az osztályokban lévő tagok felsorolását is. A funkció bemutatásához definiáljon egy egyszerű osztályt:

// 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; }
   }
};

Példa: szerelvények ellenőrzése

Ha a fenti kód egy vcpp_reflection_6.dllnevű DLL-re van lefordítva, a szerelvény tartalmának vizsgálatához használhatja a visszaverődést. Ehhez az xref:System.Reflection.Assembly.Load%2A?displayProperty=nameWithType statikus tükröződési API-függvényt kell használnia a szerelvény betöltéséhez. Ez a függvény egy szerelvényobjektum címét adja vissza, amely ezután lekérdezhető a modulokról és a benne lévő típusokról.

Miután a visszatükrözési rendszer sikeresen betölti az assembly-t, a függvény egy Assembly.GetTypes objektumokat tartalmazó tömböt kér le. Minden tömbelem más típusú információkat tartalmaz, de ebben az esetben csak egy osztály van definiálva. Hurok használatával lekérdezzük a tömb minden elemének típusát a Type::GetMembers függvénnyel. Ez a függvény a MethodInfo objektumokat tartalmazó tömböt adja vissza, amelyek a tagfüggvényről, az adattagról vagy a típusban lévő tulajdonságról tartalmaznak információkat.

Vegye figyelembe, hogy a metódusok listája tartalmazza a TestClass-ban explicit módon definiált függvényeket és a System::Object osztálytól implicit módon öröklő függvényeket. Annak részeként, hogy nem a Visual C++ szintaxisban, hanem a .NET-ben van ismertetve, a tulajdonságok a get/set függvények által elért mögöttes adattagként jelennek meg. A get/set függvények normál metódusként jelennek meg ebben a listában. A tükröződést a közös nyelvi futtatókörnyezet támogatja, nem a Microsoft C++ fordítója.

Bár ezt a kódot egy ön által definiált szerelvény vizsgálatára használta, a .NET-szerelvények vizsgálatához is használhatja ezt a kódot. Ha például a TestAssemblyt mscorlib értékre módosítja, akkor megjelenik a mscorlib.dlldefiniált összes típus és módszer listája.

// 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);
}

Útmutató: Plug-In összetevőarchitektúra implementálása a Reflection használatával

Az alábbi példakód bemutatja, hogy a tükröződés egy egyszerű "beépülő modul" architektúra implementálásához használható. Az első lista az alkalmazás, a második pedig a beépülő modul. Az alkalmazás egy több dokumentuműrlap, amely a parancssori argumentumként megadott beépülő modul DLL-ben található űrlapalapú osztályok használatával tölti ki magát.

Az alkalmazás megkísérli betölteni a megadott szerelvényt a System.Reflection.Assembly.Load módszerrel. Ha sikerül, a System.Reflection.Assembly.GetTypes metódus segítségével a típusok felsorolásra kerülnek a kódtáron belül. Ezután a rendszer ellenőrzi, hogy az egyes típusok kompatibilisek-e a System.Type.IsAssignableFrom módszerrel. Ebben a példában a megadott szerelvényben található osztályokat az Form osztályból kell származtatnia, hogy beépülő modulnak minősüljön.

A kompatibilis osztályok ezután példányosítva lesznek a System.Activator.CreateInstance metódussal, amely argumentumként fogadja el az osztályt Type , és egy mutatót ad vissza egy új példánynak. Ezután minden új példány az űrlaphoz lesz csatolva, és megjelenik.

Vegye figyelembe, hogy a metódus nem fogadja el a Load fájlkiterjesztést tartalmazó szerelvényneveket. Az alkalmazás fő függvénye levágja a megadott bővítményeket, így az alábbi példa mindkét esetben működik.

Példaalkalmazás

Az alábbi kód határozza meg a beépülő modulokat elfogadó alkalmazást. Első argumentumként szerelvénynevet kell megadni. Ennek a szerelvénynek legalább egy nyilvános Form származtatott típust kell tartalmaznia.

// 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));
}

Példa beépülő modulok

Az alábbi kód három, a Form-ból származtatott osztályt határoz meg. Ha az eredményül kapott assembly neve az előző listában a futtathatónak lesz átadva, a rendszer mind a három osztályt felderíti és példányosít, annak ellenére, hogy a fordításkor mind ismeretlenek voltak a hosztoló alkalmazás számára.

// 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);
      }
   }
};

Útmutató: Adattípusok számbavétele a szerelvényekben a Reflection használatával

Az alábbi kód bemutatja a nyilvános típusok és tagok számbavételét a használatával System.Reflection.

Egy összeállítás nevének megadása után a helyi könyvtárban vagy a GAC-ban az alábbi kód megpróbálja megnyitni az összeállítást, és lekérni a leírásokat. Sikeres végrehajtás esetén minden típus megjelenik a nyilvános tagjaival együtt.

Vegye figyelembe, hogy System.Reflection.Assembly.Load nincs szükség fájlkiterjesztésre. Ezért a "mscorlib.dll" parancssori argumentumként való használata sikertelen lesz, míg az "mscorlib" használata a .NET-keretrendszer típusok megjelenítését eredményezi. Ha nincs megadva szerelvénynév, a kód észleli és jelenti az aktuális szerelvényen belüli típusokat (a kódból eredő EXE-t).

példa

// 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);
}

Lásd még