Tipi gestiti (C++/CLI)

Visual C++ consente l'accesso alle funzionalità .NET tramite tipi gestiti, che forniscono supporto per le funzionalità di Common Language Runtime e sono soggetti ai vantaggi e alle restrizioni del runtime.

Tipi gestiti e funzione principale

Quando si scrive un'applicazione usando /clr, gli argomenti della main() funzione non possono essere di un tipo gestito.

Un esempio di firma corretta è:

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

Equivalenti di .NET Framework ai tipi nativi C++

La tabella seguente illustra le parole chiave per i tipi Visual C++ predefiniti, ovvero alias di tipi predefiniti nello spazio dei nomi System .

Tipo Visual C++ Tipo .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

Per altre informazioni sull'opzione del compilatore per impostazione predefinita su o , vedere/J (Il tipo predefinito signed char char è unsigned).unsigned char

Problemi di versione per i tipi valore annidati nei tipi nativi

Si consideri un componente assembly firmato (con nome sicuro) usato per compilare un assembly client. Il componente contiene un tipo di valore utilizzato nel client come tipo per un membro di un'unione nativa, una classe o una matrice. Se una versione futura del componente modifica le dimensioni o il layout del tipo di valore, il client deve essere ricompilato.

Creare un file di chiave con sn.exe (sn -k mykey.snk).

Esempio

L'esempio seguente è il 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);
   }
};

Questo esempio è il client:

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

L'esempio produce il seguente output:

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

Commenti

Tuttavia, se si aggiunge un altro membro a struct S in nested_value_types.cpp (ad esempio, double d;) e si ricompila il componente senza ricompilare il client, il risultato è un'eccezione non gestita (di tipo System.IO.FileLoadException).

Come verificare l'uguaglianza

Nell'esempio seguente, un test di uguaglianza che usa Estensioni gestite per C++ si basa sugli handle a cui fanno riferimento gli handle.

Esempio

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

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

Il programma mostra che il valore restituito viene implementato usando una chiamata a op_Equality.

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

Come diagnosticare e risolvere i problemi di compatibilità degli assembly

Quando la versione di un assembly a cui viene fatto riferimento in fase di compilazione non corrisponde alla versione dell'assembly a cui viene fatto riferimento in fase di esecuzione, possono verificarsi diversi problemi.

Quando un assembly viene compilato, è possibile fare riferimento ad altri assembly con la #using sintassi . Durante la compilazione, questi assembly sono accessibili dal compilatore. Le informazioni di questi assembly vengono usate per prendere decisioni di ottimizzazione.

Tuttavia, se l'assembly a cui si fa riferimento viene modificato e ricompilato, ricompilare anche l'assembly di riferimento che dipende da esso. In caso contrario, gli assembly potrebbero diventare incompatibili. Le decisioni di ottimizzazione valide inizialmente potrebbero non essere corrette per la nuova versione dell'assembly. Possono verificarsi diversi errori di runtime a causa di queste incompatibilità. In questi casi non esiste alcuna eccezione specifica. Il modo in cui l'errore viene segnalato in fase di esecuzione dipende dalla natura della modifica del codice che ha causato il problema.

Questi errori non devono essere un problema nel codice di produzione finale, purché l'intera applicazione venga ricompilata per la versione rilasciata del prodotto. Gli assembly rilasciati al pubblico devono essere contrassegnati con un numero di versione ufficiale, che garantisce che questi problemi vengano evitati. Per altre informazioni, vedere Controllo delle versioni degli assembly.

Per diagnosticare e correggere un errore di incompatibilità

È possibile che si verifichino eccezioni di runtime o altre condizioni di errore nel codice che fanno riferimento a un altro assembly. Se non è possibile identificare un'altra causa, il problema potrebbe essere un assembly non aggiornato.

  1. Prima di tutto, isolare e riprodurre l'eccezione o un'altra condizione di errore. Un problema che si verifica a causa di un'eccezione obsoleta deve essere riproducibile.

  2. Controllare il timestamp di tutti gli assembly a cui si fa riferimento nell'applicazione.

  3. Se i timestamp di qualsiasi assembly a cui si fa riferimento sono successivi al timestamp dell'ultima compilazione dell'applicazione, l'applicazione non è aggiornata. Se non è aggiornato, ricompilare l'applicazione con gli assembly più recenti e modificare il codice, se necessario.

  4. Eseguire di nuovo l'applicazione, eseguire i passaggi che riproducono il problema e verificare che l'eccezione non si verifichi.

Esempio

Il programma seguente illustra il problema: riduce innanzitutto l'accessibilità di un metodo e quindi tenta di accedere a tale metodo in un altro assembly senza ricompilare. Compilare changeaccess.cpp prima di tutto. Si tratta dell'assembly a cui si fa riferimento che cambierà. referencing.cppCompilare quindi . Dovrebbe essere compilato correttamente. Ridurre quindi l'accessibilità del metodo chiamato. Ricompilare changeaccess.cpp con l'opzione /DCHANGE_ACCESSdel compilatore . Rende il access_me metodo , anziché public, in modo che non possa essere chiamato dall'esterno Test o protecteddai suoi derivati. Senza ricompilare referencing.exe, eseguire di nuovo l'applicazione. Si verifica un oggetto MethodAccessException .

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

};

Ecco l'origine per l'assembly di riferimento:

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

Vedi anche

Programmazione .NET con C++/CLI (Visual C++)
Interoperabilità con altri linguaggi .NET (C++/CLI)
Tipi gestiti (C++/CLI)
Direttiva #using