2016-05-31 9 views
6

Voglio appuntare una matrice genericamente tipizzato e ottenere un puntatore alla sua memoria:Come posso pin (e ottenere un IntPtr a) un array T [] generico?

T[] arr = ...; 
fixed (T* ptr = arr) 
{ 
    // Use ptr here. 
} 

Ma cercando di compilare il codice di cui sopra produce questo errore di compilazione:

Cannot take the address of, get the size of, or declare a pointer to a managed type 

le risposte a thesequestions confermare che non c'è modo di dichiarare un puntatore generico T*. Ma esiste un modo per bloccare un array generico T[] e ottenere uno IntPtr nella memoria bloccata? (Tale puntatore ha ancora un utilizzo perché potrebbe essere passato al codice nativo o lanciato a un puntatore di tipo noto.)

+2

Questa domanda sta per avere un sacco di programmatori C# nei guai. C'è una grande, grande differenza tra IntPtr e T *. No, non puoi passare ciecamente l'IntPtr al codice nativo, è probabile che subisca la stessa sorte del tuo programma C#. Ma senza l'eccezione di avvertirti. È necessario eseguire il marshalling dei tipi T non banali in modo che il loro layout sia ben definito e che le conversioni dei membri siano gestite. Marshal.StructureToPtr() fa questo. Usare T [] nella dichiarazione di pinvoke è sempre ottimale, sia sicuro * che * veloce quando può essere. –

+1

@HansPassant: questi sono buoni punti da tenere a mente e si dovrebbe decisamente preferire l'utilizzo delle funzionalità di marshalling standard quando possibile. Ma in alcuni casi, non hai scelta. Ad esempio, quando si chiama un'API nativa che accetta un puntatore void * (poiché l'API può acquisire dati in formati arbitrari), così il marshalling di pinvoke non si prenderà cura di questo per te. Lavoro personalmente molto con DirectX tramite il wrapper .NetNet di SharpDX, e questo risulta * molto *. –

+0

Inoltre, ci sono casi in cui, per motivi di prestazioni (disclaimer: profilo per identificare i colli di bottiglia delle prestazioni prima di ricorrere a codice non sicuro come questo), si desidera ottenere un puntatore a un array e copiarne l'intero contenuto tutto in una volta su un puntatore di destinazione. Si potrebbe teoricamente chiamare StructureToPtr su ogni elemento dell'array, ma è molto più costoso della semplice copia dell'intero array. (E StructureToPtr crea anche garbage boxing, anche il sovraccarico generico.) –

risposta

6

Sì, è possibile utilizzare l'oggetto GCHandle per bloccare un array generico T[] e ottenere un IntPtr nella sua memoria mentre appuntato:

T[] arr = ...; 
GCHandle handle = GCHandle.Alloc(arr, GCHandleType.Pinned); 
IntPtr ptr = handle.AddrOfPinnedObject(); 
// Use the ptr. 
handle.Free(); 

assicurarsi a ricordare di chiamare Free(), perché altrimenti la matrice non diventerà mai sbloccati.

Problemi correlati