Reflexión en C++
Actualización: noviembre 2007
La reflexión permite inspeccionar tipos de datos conocidos en tiempo de ejecución. La reflexión permite igualmente enumerar los tipos de datos en un ensamblado indicado y descubrir los miembros de una clase o de un tipo de valor dados. Esto es cierto independientemente de si el tipo era conocido o se hacía referencia a él en tiempo de compilación. Esto hace que la reflexión sea una función útil para las herramientas de desarrollo y de administración de código.
Observe que el nombre del ensamblado es un nombre seguro (vea Ensamblados con nombre seguro), que incluye información de la versión de ensamblado, de referencia cultural y de firma. Observe además que puede recuperarse el nombre del espacio de nombres en el que se define el tipo de datos, al igual que el nombre de la clase base.
Lo más habitual para obtener acceso a las características de reflexión es utilizar el método GetType. Este método lo proporciona System::Object, del que derivan todas las clases de recolección de elementos no utilizados.
La reflexión en un .exe generado con el compilador de Visual C++ está permitida si el .exe se genera con las opciones del compilador /clr:pure o /clr:safe. Vea /clr (Compilación de Common Language Runtime) para obtener más información.
Temas de esta sección:
Cómo: Implementar una arquitectura de componentes complementarios mediante reflexión
Cómo: Enumerar tipos de datos en ensamblados mediante reflexión
Para obtener más información, vea
Ejemplo
El método GetType devuelve un puntero a un objeto de clase Type, que describe el tipo en el que se basa el objeto. (El objeto Type no contiene ninguna información de instancia específica). Un elemento como éste es el nombre completo del tipo, que puede mostrarse de la siguiente forma:
Observe que el nombre de tipo incluye el ámbito completo en el que se define el tipo, incluido el espacio de nombres, y que se muestra en la sintaxis de .NET con un punto como el operador de resolución de ámbito.
// 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'
Los tipos de valor pueden utilizarse también con la función GetType pero deben someterse primero a una conversión boxing.
// 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'
Al igual que con el método GetType, el operador typeid devuelve un puntero a un objeto Type, por lo que este código indica el nombre de tipo System.Int32. Mostrar los nombres de tipo es la función de reflexión más básica pero una técnica posiblemente más útil es inspeccionar o descubrir los valores válidos para los tipos enumerados. Esto puede realizarse mediante la función estática Enum::GetNames, que devuelve una matriz de cadenas, cada una de las cuales contiene un valor de enumeración en formato de texto. El ejemplo siguiente recupera una matriz de cadenas que describe los valores de enumeración de valor para la enumeración (CLR) Options y los muestra en un bucle.
Si se agrega la cuarta opción a la enumeración Options, este código informará de la nueva opción sin recompilación, incluso si la enumeración se define en un ensamblado separado.
// 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
El objeto GetType admite un número de miembros y propiedades que pueden utilizarse para examinar un tipo. Este código recupera y muestra parte de esta información:
// 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
La reflexión también permite la enumeración de tipos dentro de un ensamblado y de miembros dentro de las clases. Para demostrar esta característica, defina una clase simple:
// 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; }
}
};
Si el código anterior se compila en un archivo DLL llamado vcpp_reflection_6.dll, puede utilizar la reflexión para inspeccionar el contenido de este ensamblado. Esto implica utilizar la función API de reflexión estática Assembly::Load para cargar el ensamblado. Esta función devuelve la dirección de un objeto Assembly al que se le pueden solicitar los módulos y tipos que tiene.
Cuando el sistema de reflexión haya cargado correctamente el ensamblado, se recupera una matriz de objetos Type con la función Assembly::GetTypes. Cada elemento de la matriz contiene información sobre un tipo diferente, aunque en este caso sólo se define una clase. Mediante un bucle y utilizando la función Type::GetMembers, se solicitan los miembros de tipos a cada Type de esta matriz. Esta función devuelve una matriz de objetos MethodInfo que contienen información de la función miembro, miembro de datos o la propiedad en el tipo.
Observe que la lista de métodos incluye las funciones definidas explícitamente en TestClass y las funciones heredadas implícitamente de la clase System::Object. Por haberse descrito en .NET y no en la sintaxis de Visual C++, las propiedades aparecen como miembro de datos subyacente a los que se obtiene acceso mediante funciones get/set. Las funciones get/set aparecen en esta lista con métodos regulares. Common Language Runtime admite la reflexión, no el compilador de Visual C++.
Aunque utilice este código para examinar un ensamblado que haya definido, también puede utilizarlo para examinar ensamblados de .NET. Por ejemplo, si cambia TestAssembly por mscorlib, entonces verá una lista de todos los tipos y métodos definidos en 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);
}