Compartilhar via


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 de 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 primitivos

Considere um componente de assembly com nome forte assinado 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 mencionados usando 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, também recompile o assembly que faz referência a ele. Caso contrário, os conjuntos 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 componentes lançados ao público devem ser marcados com um número de versão oficial, garantindo assim 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.

  1. 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.

  2. Verifique o carimbo de data/hora de qualquer assembly referenciado em sua aplicação.

  3. Se os timestamps de quaisquer assemblies referenciados forem posteriores ao timestamp da última compilação do aplicativo, o aplicativo está desatualizado. Se o aplicativo estiver desatualizado, recompile-o com os assemblies mais recentes e edite seu código, se necessário.

  4. Reinicie o aplicativo. Realize 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. Deve compilar com sucesso. Em seguida, reduza a acessibilidade do método chamado. Recompile changeaccess.cpp com a opção /DCHANGE_ACCESS do compilador. Isso torna o método access_meprotected, ao invés de public, de modo que ele não pode ser chamado externamente a Test ou seus 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