Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
A reflexão permite que tipos de dados conhecidos sejam inspecionados em tempo de execução. A reflexão permite a enumeração de tipos de dados em um determinado assembly, e os membros de uma determinada classe ou tipo de valor podem ser descobertos. Isso é verdade independentemente de o tipo ser conhecido ou referenciado em tempo de compilação. Isso torna a reflexão um recurso útil para ferramentas de desenvolvimento e gerenciamento de código.
Observe que o nome do assembly fornecido é o nome forte (consulte Criando e usando assemblies Strong-Named), que inclui a versão do assembly, a cultura e as informações de assinatura. Observe também que o nome do namespace no qual o tipo de dados é definido pode ser recuperado, juntamente com o nome da classe base.
A maneira mais comum de acessar recursos de reflexão é através do GetType método. Este método é fornecido por System.Object, do qual derivam todas as classes geridas por recolha de lixo.
Observação
A reflexão sobre um .exe criado com o compilador Microsoft C++ só é permitida se o .exe for criado com as opções do compilador /clr:pure ou /clr:safe . As opções do compilador /clr:pure e /clr:safe foram preteridas no Visual Studio 2015 e não estão disponíveis no Visual Studio 2017. Consulte /clr (Common Language Runtime Compilation) para obter mais informações.
Para mais informações, consulte System.Reflection
Exemplo: GetType
O GetType método retorna um ponteiro para um Type objeto de classe, que descreve o tipo no qual o objeto é baseado. (O objeto Type não contém nenhuma informação específica da instância.) Um desses itens é o nome completo do tipo, que pode ser exibido da seguinte forma:
Observe que o nome do tipo inclui o escopo completo no qual o tipo é definido, incluindo o namespace, e que ele é exibido na sintaxe do .NET, com um ponto como o operador de resolução de escopo.
// 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'
Exemplo: tipos de valor encaixotados
Os tipos de valor também podem ser usados com a função GetType, mas devem ser encaixotados primeiro.
// 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'
Exemplo: typeid
Como com o GetType método, o operador typeid retorna um ponteiro para um objeto Type , portanto, esse código indica o nome do tipo System.Int32. A exibição de nomes de tipos é o recurso mais básico da reflexão, mas uma técnica potencialmente mais útil é inspecionar ou descobrir os valores válidos para tipos enumerados. Isso pode ser feito usando a função estática Enum::GetNames , que retorna uma matriz de cadeias de caracteres, cada uma contendo um valor de enumeração em forma de texto. O exemplo a seguir recupera uma matriz de cadeias de caracteres que descreve os valores de enumeração de valor para o enum Options (CLR) e os exibe em um loop.
Se uma quarta opção for adicionada à enumeração Options , esse código relatará a nova opção sem recompilação, mesmo que a enumeração seja definida em um assembly 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
Exemplo: membros e propriedades GetType
O GetType objeto suporta vários membros e propriedades que podem ser usados para examinar um tipo. Este código recupera e exibe algumas dessas informações:
// 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
Exemplo: enumeração de tipos
A reflexão também permite a enumeração de tipos dentro de uma assemblagem e dos membros nas classes. Para demonstrar esse recurso, defina uma classe simples:
// 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; }
}
};
Exemplo: inspeção de conjuntos
Se o código acima for compilado em uma DLL denominada vcpp_reflection_6.dll, poderá então usar *reflection* para examinar o conteúdo desse conjunto de módulos. Isso envolve o uso da função de API de reflexão estática xref:System.Reflection.Assembly.Load%2A?displayProperty=nameWithType para carregar o assembly. Esta função retorna o endereço de um objeto Assembly que pode ser consultado sobre os módulos e tipos dentro.
Uma vez que o sistema de reflexão carrega com êxito a assembly, uma matriz de objetos Type é recuperada através da função Assembly.GetTypes. Cada elemento de matriz contém informações sobre um tipo diferente, embora, neste caso, apenas uma classe seja definida. Usando um loop, cada Tipo nesta matriz é consultado sobre os membros do tipo usando a função Type::GetMembers . Essa função retorna uma matriz de objetos MethodInfo , cada objeto contendo informações sobre a função membro, membro de dados ou propriedade no tipo.
Observe que a lista de métodos inclui as funções explicitamente definidas em TestClass e as funções implicitamente herdadas da classe System::Object . Como parte de serem descritas no .NET em vez de na sintaxe do Visual C++, as propriedades surgem como o membro de dados subjacente acessado pelas funções get/set. As funções get/set aparecem nesta lista como métodos regulares. A reflexão é suportada através do Common Language Runtime, não pelo compilador Microsoft C++.
Embora você tenha usado esse código para inspecionar um assembly que você definiu, você também pode usar esse código para inspecionar assemblies .NET. Por exemplo, se você alterar TestAssembly para mscorlib, verá uma listagem de todos os tipos e métodos definidos em 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);
}
Como: Implementar uma arquitetura de componente Plug-In usando o Reflection
Os exemplos de código a seguir demonstram o uso da reflexão para implementar uma arquitetura simples de "plug-in". A primeira listagem é o aplicativo, e a segunda é o plug-in. A aplicação é um formulário de documentos múltiplos que se preenche utilizando quaisquer classes baseadas em formulário encontradas no plug-in DLL fornecido como um argumento de linha de comando.
O aplicativo tenta carregar o assembly fornecido usando o System.Reflection.Assembly.Load método. Se bem-sucedido, os tipos dentro do assembly são enumerados usando o método System.Reflection.Assembly.GetTypes. Cada tipo é então verificado quanto à compatibilidade usando o System.Type.IsAssignableFrom método. Neste exemplo, as classes encontradas no assembly fornecido devem ser derivadas da Form classe para se qualificar como um plug-in.
As classes compatíveis são então instanciadas com o System.Activator.CreateInstance método, que aceita a Type como um argumento e retorna um ponteiro para uma nova instância. Cada nova instância é então anexada ao formulário e exibida.
Observe que o Load método não aceita nomes de assembly que incluem a extensão de arquivo. A função principal no aplicativo corta todas as extensões fornecidas, portanto, o exemplo de código a seguir funciona em ambos os casos.
Aplicativo de exemplo
O código a seguir define a aplicação que aceita plug-ins. Um nome de assembly deve ser fornecido como primeiro argumento. Esta montagem deve conter pelo menos um tipo derivado público Form .
// 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));
}
Exemplos de plug-ins
O código a seguir define três classes derivadas de Form. Quando o nome do assembly resultante é passado para o executável na listagem anterior, cada uma dessas três classes será descoberta e instanciada, apesar do fato de que todas eram desconhecidas para o aplicativo de hospedagem no momento da compilação.
// 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);
}
}
};
Como Enumerar Tipos de Dados em Assemblies Usando Reflexão
O código a seguir demonstra a enumeração de tipos públicos e membros usando System.Reflection.
Dado o nome de um assembly, seja no diretório local ou no GAC, o código abaixo tenta abrir o assembly e recuperar descrições. Se for bem-sucedido, cada tipo será exibido com seus membros públicos.
Note que System.Reflection.Assembly.Load requer que nenhuma extensão de arquivo é usada. Portanto, usar "mscorlib.dll" como um argumento de linha de comando falhará, enquanto usar apenas "mscorlib" resultará na exibição dos tipos do .NET Framework. Se nenhum nome de assembly for fornecido, o código detetará e relatará os tipos dentro do assembly atual (o EXE resultante desse código).
Exemplo
// 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);
}