2013-12-16 10 views
7

Avere un metodo per convertire double[] d a IntPtr come:Fai solo un metodo di array di qualsiasi tipo di IntPtr

public static IntPtr DoubleArrayToIntPtr(double[] d) 
{ 
    IntPtr p = Marshal.AllocCoTaskMem(sizeof(double) * d.Length); 
    Marshal.Copy(d, 0, p, d.Length); 
    return p; 
} 

Quale sarebbe il modo migliore per rendere int[], float[], ecc stavo pensando a fare un metodo per ogni tipo come l'aggiunta di int []:

public static IntPtr IntArrayToIntPtr(int[] d) 
{ 
    IntPtr p = Marshal.AllocCoTaskMem(sizeof(int) * d.Length); 
    Marshal.Copy(d, 0, p, d.Length); 
    return p; 
} 

1. Potrebbe questo metodo essere generalizzato, se sì, come?

2. È possibile ottenere il puntatore in una sola riga di codice (come Marshal è il metodo void)?

+2

che cosa hai bisogno l'intptr per - che determina come verrebbe eseguito il marshalling. –

+1

Vuoi effettivamente una copia, o solo un [puntatore a un array bloccato] (http://stackoverflow.com/a/4097057/11683)? – GSerg

+0

Toolkit Modelli di trasformazione del testo. T4 in breve. – Theraot

risposta

4

L'approccio migliore sarebbe quello di fare una classe di estensione che ha fatto questo per voi, per ogni tipo. Non è possibile renderlo generico poiché Marshal.Copy() non supporta un tipo generico. Quindi si dovrà duplicare il vostro metodo per ogni tipo byte, char, int, long, float, double, ecc, tuttavia, è possibile effettuare un metodo di supporto generico per determinare la dimensione di Marshsal.

Qualcosa come di seguito potrebbe essere utile a voi:

public static class MarshalExtender 
{ 
    public static IntPtr CopyToCoTaskMem(this byte[] array) 
    { 
     var ptr = AllocArrayCoTaskMem(array); 

     Marshal.Copy(array, 0, ptr, array.Length); 
     return ptr; 
    } 

    // Copy the above method and replace types as needed (int, double, etc). 

    // Helper method for allocating bytes with generic arrays. 
    static IntPtr AllocArrayCoTaskMem<T>(T[] array) 
    { 
     var type = typeof(T); 
     var size = Marshal.SizeOf(type) * array.Length; 
     return Marshal.AllocCoTaskMem(size); 
    } 
} 

Il vantaggio principale è nel codice si potrebbe facilmente fare qualcosa di simile:

var myArray = { 0, 1, 2, 3, 4, 5, ... } 
var ptr = myArray.CopyToCoTaskMem(); 

NOTA: Basta essere sicuri di chiamare Marshal.FreeCoTaskMem() quando hai finito! Raccomanderei il metodo di estensione che restituisce un oggetto IDisposable in modo da poterlo racchiudere in un blocco using.

Ad esempio:

public sealed class CoTaskMemoryHandle : IDisposable 
{ 
    bool isDisposed; 
    readonly IntPtr handle; 

    public IntPtr Handle { get { return handle; } } 

    public CoTaskMemoryHandle(IntPtr handle) 
    { 
     this.handle = handle; 
    } 

    public void Dispose() 
    { 
     OnDispose(true); 
     GC.SuppressFinalize(this); 
    } 

    void OnDispose(bool isDisposing) 
    { 
     if (isDisposed) return; 

     if (isDisposing) 
     { 
      if (handle != IntPtr.Zero) 
       Marshal.FreeCoTaskMem(handle); 
     } 

     isDisposed = true; 
    } 
} 

Allora il tuo metodo di una classe di estensione modificata:

public static CoTaskMemoryHandle CopyToCoTaskMem(this byte[] array) 
{ 
    var ptr = AllocArrayCoTaskMem(array); 

    Marshal.Copy(array, 0, ptr, array.Length); 
    return new CoTaskMemoryHandle(ptr); 
} 

questo modo si può incapsulare in modo sicuro il bilanciamento Alloc/Free:

using(myArray.CopyToCoTaskMem()) 
{ 
    // Do something here.. 
} 
+1

Consiglierei di ereditare ['SafeHandleZeroOrMinusOneIsInvalid'] (http://msdn.microsoft.com/ru-ru/library/microsoft.win32.safehandles. safehandlezeroorminusoneisinvalid.aspx) invece di 'IDisposable'. – GSerg

+0

Sì! Stavo proprio pensando a questo! – Erik

2

Per rispondere alla tua prima domanda, sì, potrebbe essere generalizzato:

public static IntPtr ArrayToIntPtr<T>(T[] d) where T : struct 
{ 
    var arrayType = typeof(T); 
    IntPtr p = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(T)) * d.Length); 
    if (arrayType == typeof(int)) 
    { 
      Marshal.Copy((int[])(object)d, 0, p, d.Length); 
    } 
    //else if ....other types you want to handle 
    return p; 
} 

ho aggiunto un tipo di vincolo di struct, che si limita a tipi di valore (pantaloncini, doppie, ints, struct). Ovviamente dovrai occuparti di quello che passi lì (come se passi in una struttura).

Per rispondere alla seconda domanda, no, non penso che sia possibile ridurlo a una singola riga, e anche se si potesse, probabilmente non si vorrebbe per la leggibilità.

EDIT
Ho risolto i problemi individuati da Silo nei commenti, ma questo codice è ora molto brutto e non posso dire che mi sento di raccomandare questo approccio. Ovviamente comporta un sacco di controlli di tipo e boxe per ottenere il tipo giusto. In effetti, personalmente suggerirei di seguire lo answer di Silo sopra il mio, anche se ho almeno dimostrato che puoi farlo in un modo quasi generico, anche se certamente rompendo lo spirito dei generici in primo luogo.

+2

Diversi problemi con questo. Prima riceverai un avvertimento per 'sizeof' che deve essere usato in un contesto ** non sicuro ** che non è raccomandato. "Marshal.SizeOf()" dovrebbe invece essere usato. Ancora, 'Marshal.Copy' non supporta argomenti generici e questo non funzionerà. – Erik

+0

@SiLo è quello che ottengo per scrivere codice in SO e non in un vero editor C# –

+0

Nessun problema, a volte lo tendo a farlo quindi capisco. Ero curioso di questo, così ho iniziato VS per vedere. Sfortunatamente, non è così semplice. :(Sembra che siano necessari sovraccarichi separati ma potrebbero essere metodi abbastanza piccoli – Erik

Problemi correlati