反射 (C++/CLI)Reflection (C++/CLI)

反射允许在运行时检查已知的数据类型。Reflection allows known data types to be inspected at runtime. 反射允许数据类型的枚举中给定的程序集,并且可以发现给定类或值类型的成员。Reflection allows the enumeration of data types in a given assembly, and the members of a given class or value type can be discovered. 这是而不考虑是否已知或在编译时引用的类型,则返回 true。This is true regardless of whether the type was known or referenced at compile time. 这样,反射用于开发和管理工具的代码非常有用的功能。This makes reflection a useful feature for development and code management tools.

请注意,提供的程序集名称是强名称 (请参阅创建和使用具有强名称程序集),其中包括程序集版本、 区域性和签名信息。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. 另请注意,在其中定义数据类型的命名空间的名称可以检索,以及类的基类的名称。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.

若要访问的反射功能的最常见方法是通过GetType方法。The most common way to access reflection features is through the GetType method. 此方法提供的System.Object,从垃圾收集的所有类都派生的。This method is provided by System.Object, from which all garbage-collected classes derive.

备注

反射上使用 Microsoft 生成的.exeC++如果使用生成.exe 仅允许编译器 /clr: pure/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. /Clr: pure/clr: safe编译器选项都不建议使用在 Visual Studio 2015 和 Visual Studio 2017 中不可用。The /clr:pure and /clr:safe compiler options are deprecated in Visual Studio 2015 and unavailable in Visual Studio 2017. 请参阅/clr (公共语言运行时编译)有关详细信息。See /clr (Common Language Runtime Compilation) for more information.

有关详细信息,请参阅System.ReflectionFor more information, see System.Reflection

示例:GetTypeExample: GetType

GetType方法返回一个指向Type类对象,它描述当该对象所基于的类型。The GetType method returns a pointer to a Type class object, which describes the type upon when the object is based. (类型对象不包含任何特定于实例的信息。)其中一项是类型的可以按如下所示显示的完整名称:(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:

请注意类型名称包括在其中定义该类型,它包括在命名空间的完整范围,它显示在.NET 语法中,使用范围解析运算符作为一个点。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'

示例: 装箱的值类型Example: boxed value types

值类型可以与使用GetType函数,但必须首先装箱。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'

示例: typeidExample: typeid

如同GetType方法, typeid运算符将返回一个指向类型对象,因此此代码指示的类型名称System.Int32As with the GetType method, the typeid operator returns a pointer to a Type object, so this code indicates the type name System.Int32. 显示类型名称是最基本的反射功能,但可能会更有用的方法是检查或发现枚举类型的有效值。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. 这可以通过使用静态Enum::GetNames函数,它返回数组的字符串,每个都包含以文本形式的枚举值。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. 下面的示例检索有关的值枚举值的字符串数组选项(CLR) 枚举并将它们显示在循环中。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.

如果第四个选项添加到选项枚举,此代码将报告无需重新编译,即使该枚举在单独的程序集中定义的新选项。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

示例:GetType 成员和属性Example: GetType members and properties

GetType对象支持的成员和可用于检查类型的属性数。The GetType object supports a number of members and properties that can be used to examine a type. 此代码检索并显示其中一些信息: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

类型的示例: 枚举Example: enumeration of types

反射还允许在一个程序集的类型和类中的成员的枚举。Reflection also allows the enumeration of types within an assembly and the members within classes. 若要演示此功能,定义一个简单的类: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; }
   }
};

程序集的示例: 检查Example: inspection of assemblies

如果上面的代码编译为 DLL 调用 vcpp_reflection_6.dll,可以使用反射来检查此程序集的内容。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. 这涉及到使用静态反射 API 函数 xref:System.Reflection.Assembly.Load%2A?displayProperty=nameWithType 加载程序集。This involves using the static reflection API function xref:System.Reflection.Assembly.Load%2A?displayProperty=nameWithType to load the assembly. 此函数返回的地址程序集然后可以查询有关模块中的类型的对象。This function returns the address of an Assembly object that can then be queried about the modules and types within.

反射系统已成功加载程序集的数组后类型对象检索与Assembly.GetTypes函数。Once the reflection system successfully loads the assembly, an array of Type objects is retrieved with the Assembly.GetTypes function. 每个数组元素包含有关不同类型的信息,尽管这种情况下,定义一个类。Each array element contains information about a different type, although in this case, only one class is defined. 使用循环,每个类型此数组中查询有关使用的类型成员Type::GetMembers函数。Using a loop, each Type in this array is queried about the type members using the Type::GetMembers function. 此函数返回的数组MethodInfo对象,每个对象,其中包含有关成员函数、 数据成员或类型中的属性的信息。This function returns an array of MethodInfo objects, each object containing information about the member function, data member, or property in the type.

请注意,方法的列表包含函数显式中定义TestClass和函数隐式继承自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. 在.NET 中,而不是视觉对象中所描述的一部分C++语法,属性显示为 get/set 函数访问的基础数据成员。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. Get/set 函数在此列表中显示为常规方法。The get/set functions appear in this list as regular methods. 通过公共语言运行时,不是由 Microsoft 支持反射C++编译器。Reflection is supported through the common language runtime, not by the Microsoft C++ compiler.

尽管此代码用于检查你定义的程序集,但您可以使用此代码检查.NET 程序集。Although you used this code to inspect an assembly that you defined, you can also use this code to inspect .NET assemblies. 例如,如果对 mscorlib 更改 TestAssembly,然后将看到每个类型和方法在 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);
}

操作说明:实现使用反射的插件组件体系结构How to: Implement a Plug-In Component Architecture using Reflection

下面的代码示例演示如何使用反射可以实现简单的"插件"体系结构。The following code examples demonstrate the use of reflection to implement a simple "plug-in" architecture. 列的第一个部分是应用程序,第二项是插件。The first listing is the application, and the second is the plug-in. 应用程序是填充本身使用作为命令行参数提供的插件 DLL 中找到任何基于窗体的类的多个文档窗体。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.

应用程序尝试加载提供的程序集使用System.Reflection.Assembly.Load方法。The application attempts to load the provided assembly using the System.Reflection.Assembly.Load method. 如果成功,在程序集中的类型进行枚举System.Reflection.Assembly.GetTypes方法。If successful, the types inside the assembly are enumerated using the System.Reflection.Assembly.GetTypes method. 每个类型然后检查兼容性使用System.Type.IsAssignableFrom方法。Each type is then checked for compatibility using the System.Type.IsAssignableFrom method. 在此示例中,找到中提供的程序集的类必须派生自Form类才会被视为一个插件。In this example, classes found in the provided assembly must be derived from the Form class to qualify as a plug-in.

使用然后实例化兼容类System.Activator.CreateInstance方法,用于接受Type作为参数并返回一个指向新实例。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. 然后附加到窗体,显示每个新实例。Each new instance is then attached to the form and displayed.

请注意,Load方法不接受包含文件扩展名的程序集名称。Note that the Load method does not accept assembly names that include the file extension. 因此下面的代码示例适用于在任一情况下,应用程序中的主函数裁剪掉任何提供的扩展。The main function in the application trims any provided extensions, so the following code example works in either case.

示例Example

下面的代码定义接受插件的应用程序。程序集名称必须作为第一个参数提供。The following code defines the application that accepts plug-ins. An assembly name must be provided as the first argument. 此程序集应包含至少一个公共Form派生类型。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));
}

示例Example

下面的代码定义了三个类派生自FormThe following code defines three classes derived from Form. 生成的程序集名称的名称传递给前面列表中的可执行文件,这三个类的每个将发现并实例化,它们是所有托管应用程序在编译时未知的情况。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);
      }
   }
};

操作说明:枚举中使用反射的程序集的数据类型How to: Enumerate Data Types in Assemblies using Reflection

下面的代码演示的公共类型和成员使用枚举System.ReflectionThe following code demonstrates the enumeration of public types and members using System.Reflection.

给定的程序集名称,在本地目录中或在 GAC 中,尝试打开程序集并检索说明下面的代码。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. 如果成功,其公共成员显示每个类型。If successful, each type is displayed with its public members.

请注意,System.Reflection.Assembly.Load要求使用文件没有扩展名。Note that System.Reflection.Assembly.Load requires that no file extension is used. 因此,使用"mscorlib.dll"作为命令行自变量时将失败,使用只是"mscorlib"将会生成.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. 如果未不提供任何程序集名称,代码将检测并报告 (此代码生成的 EXE) 的当前程序集中的类型。If no assembly name is provided, the code will detect and report the types within the current assembly (the EXE resulting from this code).

示例Example

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

请参阅See also