Réflexion (C++/CLI)Reflection (C++/CLI)

La réflexion permet d’inspecter les types de données connus au moment de l’exécution.Reflection allows known data types to be inspected at runtime. La réflexion permet l’énumération des types de données dans un assembly donné, et les membres d’une classe ou d’un type valeur donnés peuvent être découverts.Reflection allows the enumeration of data types in a given assembly, and the members of a given class or value type can be discovered. Cela est vrai que le type soit connu ou référencé au moment de la compilation.This is true regardless of whether the type was known or referenced at compile time. Cela fait de la réflexion une fonctionnalité utile pour le développement et les outils de gestion du code.This makes reflection a useful feature for development and code management tools.

Notez que le nom d’assembly fourni est le nom fort (consultez création et utilisation d’assemblys Strong-Named), qui comprend la version, la culture et les informations de signature de l’assembly.Note that the assembly name provided is the strong name (see Creating and Using Strong-Named Assemblies), which includes the assembly version, culture, and signing information. Notez également que le nom de l’espace de noms dans lequel le type de données est défini peut être récupéré, ainsi que le nom de la classe de base.Note also that the name of the namespace in which the data type is defined can be retrieved, along with the name of the base class.

La méthode la plus courante pour accéder aux fonctionnalités de réflexion consiste à utiliser la GetType méthode.The most common way to access reflection features is through the GetType method. Cette méthode est fournie par System.Object , à partir de laquelle dérivent toutes les classes récupérées par le garbage collector.This method is provided by System.Object, from which all garbage-collected classes derive.

Notes

La réflexion sur un fichier. exe généré avec le compilateur Microsoft C++ est autorisée uniquement si le fichier. exe est généré avec les options de compilateur /clr : pure ou /clr : safe .Reflection on an .exe built with the Microsoft C++ compiler is only allowed if the .exe is built with the /clr:pure or /clr:safe compiler options. Les options de compilateur /clr : pure et /clr : safe sont dépréciées dans Visual Studio 2015 et ne sont pas disponibles dans Visual Studio 2017.The /clr:pure and /clr:safe compiler options are deprecated in Visual Studio 2015 and unavailable in Visual Studio 2017. Pour plus d’informations , consultez/CLR (compilation pour le Common Language Runtime) .See /clr (Common Language Runtime Compilation) for more information.

Pour plus d'informations, consultez System.ReflectionFor more information, see System.Reflection

Exemple : GetTypeExample: GetType

La GetType méthode retourne un pointeur vers un Type objet de classe, qui décrit le type sur lequel l’objet est basé.The GetType method returns a pointer to a Type class object, which describes the type upon when the object is based. (L’objet type ne contient pas d’informations spécifiques à l’instance.) L’un de ces éléments est le nom complet du type, qui peut être affiché comme suit :(The Type object does not contain any instance-specific information.) One such item is the full name of the type, which can be displayed as follows:

Notez que le nom de type inclut la portée complète dans laquelle le type est défini, y compris l’espace de noms, et qu’il est affiché dans la syntaxe .NET, avec un point comme opérateur de résolution de portée.Note that the type name includes the full scope in which the type is defined, including the namespace, and that it is displayed in .NET syntax, with a dot as the scope resolution operator.

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

Exemple : types valeur boxedExample: boxed value types

Les types valeur peuvent également être utilisés avec la GetType fonction, mais ils doivent tout d’abord être boxed.Value types can be used with the GetType function as well, but they must be boxed first.

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

Exemple : typeidExample: typeid

Comme avec la GetType méthode, l’opérateur typeid retourne un pointeur vers un objet de type , donc ce code indique le nom de type System. Int32.As with the GetType method, the typeid operator returns a pointer to a Type object, so this code indicates the type name System.Int32. L’affichage des noms de types est la fonctionnalité de réflexion la plus simple, mais une technique potentiellement plus utile consiste à inspecter ou à découvrir les valeurs valides pour les types énumérés.Displaying type names is the most basic feature of reflection, but a potentially more useful technique is to inspect or discover the valid values for enumerated types. Pour ce faire, vous pouvez utiliser la fonction static enum :: GetNames , qui retourne un tableau de chaînes, chacune contenant une valeur d’énumération sous forme de texte.This can be done by using the static Enum::GetNames function, which returns an array of strings, each containing an enumeration value in text form. L’exemple suivant récupère un tableau de chaînes qui décrit les valeurs d’énumération de valeur pour l’énumération d' options (CLR) et les affiche dans une boucle.The following sample retrieves an array of strings that describes the value enumeration values for the Options (CLR) enum and displays them in a loop.

Si une quatrième option est ajoutée à l’énumération options , ce code signale la nouvelle option sans recompilation, même si l’énumération est définie dans un assembly distinct.If a fourth option is added to the Options enumeration, this code will report the new option without recompilation, even if the enumeration is defined in a separate assembly.

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

Exemple : membres et propriétés GetTypeExample: GetType members and properties

L' GetType objet prend en charge un certain nombre de membres et de propriétés qui peuvent être utilisés pour examiner un type.The GetType object supports a number of members and properties that can be used to examine a type. Ce code permet de récupérer et d’afficher certaines de ces informations :This code retrieves and displays some of this information:

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

Exemple : énumération de typesExample: enumeration of types

La réflexion autorise également l’énumération des types dans un assembly et les membres au sein des classes.Reflection also allows the enumeration of types within an assembly and the members within classes. Pour illustrer cette fonctionnalité, définissez une classe simple :To demonstrate this feature, define a simple class:

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

Exemple : inspection des assemblysExample: inspection of assemblies

Si le code ci-dessus est compilé en une DLL appelée vcpp_reflection_6.dll, vous pouvez utiliser la réflexion pour inspecter le contenu de cet assembly.If the code above is compiled into a DLL called vcpp_reflection_6.dll, you can then use reflection to inspect the contents of this assembly. Cela implique l’utilisation de la fonction d’API de réflexion statique XREF : System. Reflection. assembly. Load% 2A ? displayProperty = nameWithType pour charger l’assembly.This involves using the static reflection API function xref:System.Reflection.Assembly.Load%2A?displayProperty=nameWithType to load the assembly. Cette fonction retourne l’adresse d’un objet assembly qui peut ensuite être interrogé à propos des modules et des types dans.This function returns the address of an Assembly object that can then be queried about the modules and types within.

Une fois que le système de réflexion a chargé avec succès l’assembly, un tableau d’objets de type est récupéré avec la Assembly.GetTypes fonction.Once the reflection system successfully loads the assembly, an array of Type objects is retrieved with the Assembly.GetTypes function. Chaque élément de tableau contient des informations sur un type différent, même si, dans ce cas, une seule classe est définie.Each array element contains information about a different type, although in this case, only one class is defined. À l’aide d’une boucle, chaque type de ce tableau est interrogé sur les membres de type à l’aide de la fonction type :: GetMembers .Using a loop, each Type in this array is queried about the type members using the Type::GetMembers function. Cette fonction retourne un tableau d’objets MethodInfo , chaque objet contenant des informations sur la fonction membre, le membre de données ou la propriété dans le type.This function returns an array of MethodInfo objects, each object containing information about the member function, data member, or property in the type.

Notez que la liste des méthodes comprend les fonctions explicitement définies dans TestClass et les fonctions héritées implicitement de la classe System :: Object .Note that the list of methods includes the functions explicitly defined in TestClass and the functions implicitly inherited from the System::Object class. Dans le cadre de la description dans .NET plutôt que dans la syntaxe Visual C++, les propriétés apparaissent en tant que membre de données sous-jacent accessible par les fonctions d’extraction/définition.As part of being described in .NET rather than in Visual C++ syntax, properties appear as the underlying data member accessed by the get/set functions. Les fonctions d’extraction/définition apparaissent dans cette liste comme des méthodes normales.The get/set functions appear in this list as regular methods. La réflexion est prise en charge par le common language runtime, et non par le compilateur Microsoft C++.Reflection is supported through the common language runtime, not by the Microsoft C++ compiler.

Bien que vous ayez utilisé ce code pour inspecter un assembly que vous avez défini, vous pouvez également utiliser ce code pour inspecter les assemblys .NET.Although you used this code to inspect an assembly that you defined, you can also use this code to inspect .NET assemblies. Par exemple, si vous remplacez TestAssembly par mscorlib, vous verrez une liste de tous les types et méthodes définis dans mscorlib.dll.For example, if you change TestAssembly to mscorlib, then you will see a listing of every type and method defined in 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);
}

Comment : implémenter une architecture de composant Plug-In à l’aide de la réflexionHow to: Implement a Plug-In Component Architecture using Reflection

Les exemples de code suivants illustrent l’utilisation de la réflexion pour implémenter une architecture « plug-in » simple.The following code examples demonstrate the use of reflection to implement a simple "plug-in" architecture. La première liste est l’application, et la seconde est le plug-in.The first listing is the application, and the second is the plug-in. L’application est un formulaire à plusieurs documents qui se remplit à l’aide de toutes les classes basées sur des formulaires de la DLL de plug-in fournie en tant qu’argument de ligne de commande.The application is a multiple document form that populates itself using any form-based classes found in the plug-in DLL provided as a command-line argument.

L’application tente de charger l’assembly fourni à l’aide de la System.Reflection.Assembly.Load méthode.The application attempts to load the provided assembly using the System.Reflection.Assembly.Load method. En cas de réussite, les types à l’intérieur de l’assembly sont énumérés à l’aide de la System.Reflection.Assembly.GetTypes méthode.If successful, the types inside the assembly are enumerated using the System.Reflection.Assembly.GetTypes method. La compatibilité de chaque type est ensuite vérifiée à l’aide de la System.Type.IsAssignableFrom méthode.Each type is then checked for compatibility using the System.Type.IsAssignableFrom method. Dans cet exemple, les classes trouvées dans l’assembly fourni doivent être dérivées de la Form classe pour être qualifiées de plug-in.In this example, classes found in the provided assembly must be derived from the Form class to qualify as a plug-in.

Les classes compatibles sont ensuite instanciées à l’aide de la System.Activator.CreateInstance méthode, qui accepte un Type comme argument et retourne un pointeur vers une nouvelle instance.Compatible classes are then instantiated with the System.Activator.CreateInstance method, which accepts a Type as an argument and returns a pointer to a new instance. Chaque nouvelle instance est ensuite attachée au formulaire et affichée.Each new instance is then attached to the form and displayed.

Notez que la Load méthode n’accepte pas les noms d’assemblys qui incluent l’extension de fichier.Note that the Load method does not accept assembly names that include the file extension. La fonction main de l’application supprime toutes les extensions fournies, de sorte que l’exemple de code suivant fonctionne dans les deux cas.The main function in the application trims any provided extensions, so the following code example works in either case.

ExempleExample

Le code suivant définit l’application qui accepte les plug-ins. Un nom d’assembly doit être fourni comme premier argument.The following code defines the application that accepts plug-ins. An assembly name must be provided as the first argument. Cet assembly doit contenir au moins un Form type dérivé public.This assembly should contain at least one public Form derived type.

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

ExempleExample

Le code suivant définit trois classes dérivées de Form .The following code defines three classes derived from Form. Lorsque le nom de l’assembly résultant est passé au fichier exécutable dans la liste précédente, chacune de ces trois classes sera découverte et instanciée, bien qu’elles soient toutes inconnues de l’application d’hébergement au moment de la compilation.When the name of the resulting assembly name is passed to the executable in the previous listing, each of these three classes will be discovered and instantiated, despite the fact that they were all unknown to the hosting application at compile time.

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

Comment : énumérer des types de données dans des assemblys à l’aide de la réflexionHow to: Enumerate Data Types in Assemblies using Reflection

Le code suivant illustre l’énumération des types et des membres publics à l’aide de System.Reflection .The following code demonstrates the enumeration of public types and members using System.Reflection.

À partir du nom d’un assembly, dans le répertoire local ou dans le GAC, le code ci-dessous tente d’ouvrir l’assembly et de récupérer les descriptions.Given the name of an assembly, either in the local directory or in the GAC, the code below attempts to open the assembly and retrieve descriptions. En cas de réussite, chaque type est affiché avec ses membres publics.If successful, each type is displayed with its public members.

Notez que System.Reflection.Assembly.Load requiert l’utilisation d’une extension de fichier.Note that System.Reflection.Assembly.Load requires that no file extension is used. Par conséquent, l’utilisation de « mscorlib.dll » comme argument de ligne de commande échoue, tandis que l’utilisation de « mscorlib » se traduira par l’affichage des types de .NET Framework.Therefore, using "mscorlib.dll" as a command-line argument will fail, while using just "mscorlib" will result the display of the .NET Framework types. Si aucun nom d’assembly n’est fourni, le code détecte et signale les types dans l’assembly actuel (le fichier EXE qui résulte de ce code).If no assembly name is provided, the code will detect and report the types within the current assembly (the EXE resulting from this code).

ExempleExample

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

Voir aussiSee also