2009-11-06 4 views
6

Ho un programma C# che deve passare un buffer di caratteri a una funzione non gestita. Ho trovato due modi che sembrano funzionare in modo affidabile, ma non sono sicuro di quale scegliere.Chiamare la funzione non gestita da C#: devo passare a StringBuilder o usare un codice non sicuro?

Ecco la firma della funzione non gestita.

extern "C" __declspec(dllexport) int getNextResponse(char *buffer); 

La prima opzione è definire il buffer come StringBuilder, come indicato di seguito.

//at class level... 
[DllImport("mydll.dll")] 
static extern int getNextResponse(StringBuilder buffer); 

//in main method body... 
StringBuilder sb = new StringBuilder(" ", 65536); 
int rc = getNextResponse(sb); 

Questo è semplice, e funziona, e penso che io in fondo capisco perché funziona perché lo StringBuilder ha un buffer di dietro le quinte, in modo (presumo) lo strato di interoperabilità è solo schierando lo StringBuilder ad un char *.

L'altra opzione utilizza codice non sicuro.

//at class level... 
[DllImport("mydll.dll")] 
static extern int getNextResponse(byte* buffer); 

//separate method... 
private static unsafe int runGetNextResponse(byte[] buffer) 
{ 
    fixed (byte* p = buffer) 
    { 
     int rc = getNextResponse(p); 
     return rc; 
    }    
} 

//in main method body... 
byte[] b = new byte[65536]; 
int rc = runGetNextResponse(b); 

Il secondo approccio è più codice, ma è anche più esplicito su cosa sta succedendo.

Questi due approcci sono fondamentalmente la stessa cosa? C'è qualche ragione per scegliere l'una rispetto all'altra?

risposta

8

Preferisco decisamente utilizzare la versione StringBuilder.

Non ci sarà un'enorme differenza tra i due, e l'uso di codice non sicuro non è altrettanto pulito.

A mio parere, poiché esiste un modo per risolvere il problema utilizzando una classe di libreria di base, l'utilizzo di codice non sicuro senza un chiaro (e necessario) vantaggio è un'ottimizzazione prematura.

+0

Cosa succede se il Garbage Collector rialloca lo StringBuilder passato senza un'istruzione fissa durante l'esecuzione getNextResponse? Ogni parametro del puntatore utilizzato nelle funzioni importate deve essere fisso? O mi manca seomthing? – Luca

2

Mentre non posso pesare definitivamente, posso condividere le mie esperienze. Ho usato esclusivamente il metodo StringBuilder e non ho avuto problemi con esso. Mi piace il codice più semplice di esso e l'evitamento di non sicuro.

3

Mentre è preferibile utilizzare un oggetto StringBuilder, c'è un avvertimento. Immaginate per esempio che nel metodo getNextResponse di memorizzare il puntatore a qualche variabile statica e utilizzarlo in un altro metodo:

char* globalPointer; 

int getNextResponse(char *buffer) { 
    globalPointer = buffer; 
    return 0; 
} 

void someOtherMethod() { 
    printf("%s\n", globalPointer); 
} 

Ora diamo un'occhiata al lato gestito:

var sb = new StringBuilder(); 
sb.Append("Hello World"); 
int result = getNextResponse(sb); 
Console.WriteLine(result); 
someOtherMethod(); // kaboom: The GC could have already destroyed the string builder. 

Il metodo non sicuro si garantisce che la posizione di memoria non verrà spostato:

byte[] buffer = Encoding.UTF8.GetBytes("Hello World"); 
fixed (byte* p = buffer) 
{ 
    int result = getNextResponse(p); 
    Console.WriteLine(result); 
    someOtherMethod(); // works fine as the buffer address is pinned down in memory 
} 

In questo caso la versione non sicura funzionerà meglio.

0

Dipende da cosa sono effettivamente i dati nel buffer. Se si tratta di dati di carattere, utilizzare l'approccio StringBuilder. Se si tratta di dati binari, utilizzare invece una matrice di byte, ma non utilizzare il metodo non sicuro. Mentre la paura esagerata di "non sicuro" che si sta diffondendo è un po 'sciocca e non vi è alcun motivo per non usarla quando è giustificato, in questo caso non è necessario. Utilizzare:

//at class level... 
[DllImport("mydll.dll")] 
static extern int getNextResponse([In, Out] byte[] buffer); 

//in main method body... 
byte[] buffer = new byte[65536]; 
int rc = getNextResponse(buffer); 
1

Dipende dal costo di marshalling. Se si esegue molto marshalling o se i dati sottoposti a marshalling sono grandi, è possibile che si desideri riutilizzare il buffer anziché creare/distruggere il buffer del generatore di stringhe ogni volta.

Problemi correlati