Tipos administrados (C++/CLI)

Visual C++ permite el acceso a las características de .NET a través de tipos administrados, que proporcionan compatibilidad con características de Common Language Runtime y están sujetas a las ventajas y restricciones del entorno de ejecución.

Tipos administrados y la función principal

Al escribir una aplicación mediante /clr, los argumentos de la función main() no pueden ser de un tipo administrado.

Un ejemplo de una firma adecuada es el siguiente:

// managed_types_and_main.cpp
// compile with: /clr
int main(int, char*[], char*[]) {}

Equivalentes de .NET Framework a tipos nativos de C++

En la siguiente tabla se muestran las palabras clave para los tipos de Visual C++ integrados, que son alias de los tipos predefinidos en el espacio de nombres 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 y signed short System.Int16
unsigned short System.UInt16
int, signed int, long y signed long. System.Int32
unsigned int y unsigned long System.UInt32
__int64 y signed __int64 System.Int64
unsigned __int64 System.UInt64
float System.Single
double y long double System.Double

Para más información sobre la opción del compilador para que el valor predeterminado sea signed char o unsigned char, vea /J (el tipo predeterminado char es unsigned).

Problemas de versión para los tipos de valor anidados en tipos nativos

Considere un componente de ensamblado firmado (nombre seguro) que se usa para compilar un ensamblado de cliente. El componente contiene un tipo de valor que se usa en el cliente como tipo para un miembro de una unión nativa, una clase o una matriz. Si una versión futura del componente cambia el tamaño o el diseño del tipo de valor, se debe volver a compilar el cliente.

Cree un archivo de claves con sn.exe (sn -k mykey.snk).

Ejemplo

El ejemplo siguiente es el 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 ejemplo es el 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();
}

El ejemplo produce la siguiente salida:

S.i = 5
S.i = 6
S.i = 10
S.i = 11

Comentarios

Pero si agrega otro miembro a struct S en nested_value_types.cpp (por ejemplo, double d;) y vuelve a compilar el componente sin volver a compilar el cliente, el resultado es una excepción no controlada (de tipo System.IO.FileLoadException).

Cómo probar la igualdad

En el ejemplo siguiente, una prueba de igualdad que usa Extensiones administradas para C++ se basa en a lo que hacen referencia los identificadores.

Ejemplo

// mcppv2_equality_test.cpp
// compile with: /clr /LD
using namespace System;

bool Test1() {
   String ^ str1 = "test";
   String ^ str2 = "test";
   return (str1 == str2);
}

El IL de este programa muestra que el valor devuelto se implementa mediante una llamada a op_Equality.

IL_0012:  call       bool [mscorlib]System.String::op_Equality(string, string)

Cómo diagnosticar y resolver los problemas de compatibilidad de los ensamblados

Cuando la versión de un ensamblado al que se hace referencia en tiempo de compilación no coincide con la versión del ensamblado al que se hace referencia en tiempo de ejecución, pueden producirse varios problemas.

Cuando se compila un ensamblado, se puede hacer referencia a otros ensamblados con la sintaxis #using. Durante la compilación, el compilador tiene acceso a estos ensamblados. La información de estos ensamblados se usa para tomar decisiones de optimización.

Pero si se cambia y se vuelve a compilar el ensamblado de referencia, vuelva a compilar también el ensamblado que hace referencia que depende de él. De lo contrario, los ensamblados podrían ser incompatibles. Es posible que las decisiones de optimización que eran válidas al principio no sean correctas para la nueva versión del ensamblado. Pueden producirse varios errores en tiempo de ejecución debido a estas incompatibilidades. No se produce ninguna excepción específica en estos casos. La forma en que se notifica el error en tiempo de ejecución depende de la naturaleza del cambio de código que produjo el problema.

Estos errores no deben ser un problema en el código de producción final, siempre y cuando se recompile toda la aplicación para la versión publicada del producto. Los ensamblados que se liberan al público deben marcarse con un número de versión oficial, lo que garantizará que se eviten estos problemas. Para obtener más información, vea Versiones de los ensamblados.

Para diagnosticar y corregir un error de incompatibilidad

Puede encontrar excepciones en tiempo de ejecución u otras condiciones de error en el código que hace referencia a otro ensamblado. Si no puede identificar otra causa, el problema puede ser un ensamblado no actualizado.

  1. En primer lugar, aísle y reproduzca la excepción u otra condición de error. Un problema que se produce debido a una excepción obsoleta debe ser reproducible.

  2. Compruebe la marca de tiempo de los ensamblados a los que se hace referencia en la aplicación.

  3. Si las marcas de tiempo de los ensamblados a los que se hace referencia son posteriores a la marca de tiempo de la última compilación de la aplicación, la aplicación no está actualizada. Si no está actualizada, vuelva a compilar la aplicación con los ensamblados más recientes y edite el código si es necesario.

  4. Vuelva a ejecutar la aplicación, realice los pasos que reproducen el problema y compruebe que no se produce la excepción.

Ejemplo

El siguiente programa ilustra el problema: primero reduce la accesibilidad de un método y, después, intenta tener acceso a ese método en otro ensamblado sin volver a compilarlo. Primero, compile changeaccess.cpp. Es el ensamblado de referencia que cambiará. Luego,compile referencing.cpp. Debería compilarse correctamente. Después, reduzca la accesibilidad del método llamado. Vuelva a compilar changeaccess.cpp con la opción del compilador /DCHANGE_ACCESS. Hace que el método access_me se vuelva protected, en lugar de public, por lo que no se puede llamar desde fuera de Test o sus derivados. Sin volver a compilar referencing.exe, vuelva a ejecutar la aplicación. MethodAccessException tiene lugar.

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

};

Este es el origen del ensamblado que hace referencia:

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

Consulte también

Programación de .NET con C++/CLI
Interoperabilidad con otros lenguajes de .NET (C++/CLI)
Tipos administrados (C++/CLI)
Directiva #using