Tipos gerenciados (C++/CLI)
O Visual C++ permite o acesso aos recursos do .NET por meio de tipos gerenciados, que dão suporte a recursos do common language runtime e estão sujeitos às vantagens e restrições do runtime.
Tipos gerenciados e a função principal
Quando você escreve um aplicativo usando /clr
, os argumentos da função main()
não podem ser de um tipo gerenciado.
Um exemplo de uma assinatura adequada é:
// managed_types_and_main.cpp
// compile with: /clr
int main(int, char*[], char*[]) {}
Equivalentes do .NET Framework a tipos nativos do C++
A tabela a seguir mostra as palavras-chave para tipos internos do Visual C++, que são aliases de tipos predefinidos no namespace System.
Tipo do Visual C++ | Tipo de .NET Framework |
---|---|
void |
System.Void |
bool |
System.Boolean |
signed char |
System.SByte |
unsigned char |
System.Byte |
wchar_t |
System.Char |
short e signed short |
System.Int16 |
unsigned short |
System.UInt16 |
int , signed int , long e signed long |
System.Int32 |
unsigned int e unsigned long |
System.UInt32 |
__int64 e signed __int64 |
System.Int64 |
unsigned __int64 |
System.UInt64 |
float |
System.Single |
double e long double |
System.Double |
Para obter mais informações sobre a opção do compilador para o padrão signed char
ou unsigned char
, confira /J
(o tipo char
padrão é unsigned
).
Problemas de versão para tipos de valor aninhados em tipos nativos
Considere um componente de assembly assinado (nome forte) usado para criar um assembly de cliente. O componente contém um tipo de valor que é usado no cliente como o tipo para um membro de uma união nativa, uma classe ou uma matriz. Se uma versão futura do componente alterar o tamanho ou o layout do tipo de valor, o cliente precisará ser recompilado.
Crie um arquivo de chave com sn.exe (sn -k mykey.snk
).
Exemplo
O exemplo a seguir é o componente.
// nested_value_types.cpp
// compile with: /clr /LD
using namespace System::Reflection;
[assembly:AssemblyVersion("1.0.0.*"),
assembly:AssemblyKeyFile("mykey.snk")];
public value struct S {
int i;
void Test() {
System::Console::WriteLine("S.i = {0}", i);
}
};
Este exemplo é o cliente:
// nested_value_types_2.cpp
// compile with: /clr
#using <nested_value_types.dll>
struct S2 {
S MyS1, MyS2;
};
int main() {
S2 MyS2a, MyS2b;
MyS2a.MyS1.i = 5;
MyS2a.MyS2.i = 6;
MyS2b.MyS1.i = 10;
MyS2b.MyS2.i = 11;
MyS2a.MyS1.Test();
MyS2a.MyS2.Test();
MyS2b.MyS1.Test();
MyS2b.MyS2.Test();
}
O exemplo produz essa saída:
S.i = 5
S.i = 6
S.i = 10
S.i = 11
Comentários
No entanto, se você adicionar outro membro a struct S
em nested_value_types.cpp
(por exemplo, double d;
) e recompilar o componente sem recompilar também o cliente, o resultado será uma exceção sem tratamento (do tipo System.IO.FileLoadException).
Como testar quanto a igualdade
No exemplo a seguir, um teste para igualdade que usa Managed Extensions for C++ é baseado em a que os identificadores se referem.
Exemplo
// mcppv2_equality_test.cpp
// compile with: /clr /LD
using namespace System;
bool Test1() {
String ^ str1 = "test";
String ^ str2 = "test";
return (str1 == str2);
}
O IL para este programa mostra que o valor retornado é implementado usando uma chamada para op_Equality
.
IL_0012: call bool [mscorlib]System.String::op_Equality(string, string)
Como diagnosticar e corrigir problemas de compatibilidade do assembly
Quando a versão de um assembly referenciado em tempo de compilação não corresponde à versão do assembly referenciado em runtime, vários problemas podem ocorrer.
Quando um assembly é compilado, outros assemblies podem ser referenciados com a sintaxe #using
. Durante a compilação, esses assemblies são acessados pelo compilador. As informações desses assemblies são usadas para tomar decisões de otimização.
No entanto, se o assembly referenciado for alterado e recompilado, recompile o assembly de referência que depende dele. Caso contrário, os assemblies podem se tornar incompatíveis. As decisões de otimização que eram válidas no início podem não ser corretas para a nova versão do assembly. Vários erros de runtime podem ocorrer devido a essas incompatibilidades. Não há exceção específica produzida nesses casos. A forma como a falha é relatada em runtime depende da natureza da alteração de código que causou o problema.
Esses erros não devem ser um problema no código de produção final, desde que todo o aplicativo seja recriado para a versão lançada do produto. Os assemblies lançados ao público devem ser marcados com um número de versão oficial, o que garantirá que esses problemas sejam evitados. Para obter mais informações, consulte Controle de versão do assembly.
Para diagnosticar e corrigir um erro de incompatibilidade
Você pode encontrar exceções de runtime ou outras condições de erro no código que fazem referência a outro assembly. Se você não conseguir identificar outra causa, o problema poderá ser um assembly desatualizado.
Primeiro, isole e reproduza a exceção ou outra condição de erro. Um problema que ocorre devido a uma exceção desatualizada deve ser reproduzível.
Verifique o carimbo de data/hora de todos os assemblies referenciados em seu aplicativo.
Se os carimbos de data/hora de quaisquer assemblies referenciados forem posteriores ao carimbo de data/hora da última compilação do aplicativo, o aplicativo estará desatualizado. Se o aplicativo estiver desatualizado, recompile-o com os assemblies mais recentes e edite seu código, se necessário.
Execute novamente o aplicativo e as etapas que reproduzem o problema e verifique se a exceção não ocorre.
Exemplo
O seguinte programa ilustra o problema: ele primeiro reduz a acessibilidade de um método e tenta acessar esse método em outro assembly sem recompilar. Compile changeaccess.cpp
primeiro. Esse é o assembly referenciado que será alterado. Em seguida, compile referencing.cpp
. Ele deve ser compilado com êxito. Depois, reduza a acessibilidade do método chamado. Recompile changeaccess.cpp
com a opção /DCHANGE_ACCESS
do compilador. Isso torna o método access_me
protected
em vez de public
, de modo que ele não pode ser chamado de fora de Test
ou derivados. Sem recompilar referencing.exe
, execute novamente o aplicativo. Um MethodAccessException ocorre.
// changeaccess.cpp
// compile with: /clr:safe /LD
// After the initial compilation, add /DCHANGE_ACCESS and rerun
// referencing.exe to introduce an error at runtime. To correct
// the problem, recompile referencing.exe
public ref class Test {
#if defined(CHANGE_ACCESS)
protected:
#else
public:
#endif
int access_me() {
return 0;
}
};
Aqui está a origem do assembly de referência:
// referencing.cpp
// compile with: /clr:safe
#using <changeaccess.dll>
// Force the function to be inline, to override the compiler's own
// algorithm.
__forceinline
int CallMethod(Test^ t) {
// The call is allowed only if access_me is declared public
return t->access_me();
}
int main() {
Test^ t = gcnew Test();
try
{
CallMethod(t);
System::Console::WriteLine("No exception.");
}
catch (System::Exception ^ e)
{
System::Console::WriteLine("Exception!");
}
return 0;
}
Confira também
Programação do .NET com C++/CLI (Visual C++)
Interoperabilidade com outras linguagens .NET (C++/CLI)
Tipos gerenciados (C++/CLI)
diretiva #using