2009-11-06 14 views
6

Tengo un programa C# que necesita pasar un búfer char a una función no administrada. He encontrado dos maneras que parecen funcionar de manera confiable, pero no estoy seguro de cuál elegir.Llamar a la función no administrada desde C#: ¿debería pasar StringBuilder o utilizar un código inseguro?

Aquí está la firma de la función no administrada.

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

La primera opción es definir el búfer como un StringBuilder, de la siguiente manera.

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

Esto es simple, y funciona, y creo que básicamente entiendo por qué funciona debido a que el StringBuilder tiene un buffer detrás de las escenas, por lo que (supongo) la capa de interoperabilidad se acaba el guiado de la StringBuilder a un char *.

La otra opción es usar código inseguro.

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

El segundo enfoque es más código, pero también es más explícito sobre lo que está sucediendo.

¿Estos dos enfoques son básicamente lo mismo? ¿Hay alguna razón para elegir uno sobre el otro?

Respuesta

8

Preferiría mucho usar la versión de StringBuilder.

No va a haber una gran diferencia entre los dos, y el uso de un código inseguro no es tan claro.

En mi opinión, dado que hay una manera de resolver el problema utilizando una clase de biblioteca central, el uso de código inseguro sin un beneficio claro (y necesario) es una optimización prematura.

+0

¿Qué ocurre si el recolector de basura reasigna el StringBuilder pasado sin una instrucción fija durante la ejecución getNextResponse? ¿Se deben fijar todos los parámetros de puntero utilizados en las funciones importadas? ¿O me estoy perdiendo algo? – Luca

2

Aunque no puedo opinar de manera definitiva, puedo compartir mis propias experiencias. He utilizado el método StringBuilder exclusivamente y no he tenido problemas con él. Me gusta el código más simple y la prevención de inseguridad.

3

Si bien se prefiere utilizar StringBuilder, hay una advertencia. Imagínese, por ejemplo, que en su método getNextResponse almacenar el puntero a una variable estática y utilizarlo en otro método:

char* globalPointer; 

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

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

Ahora vamos a ver el lado gestionado:

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. 

El método inseguro que garantiza que la posición de la memoria no se moverá en torno a:

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 
} 

En este caso la versión insegura va a funcionar mejor.

0

Depende de lo que realmente son los datos en el búfer. Si se trata de datos de caracteres, use el enfoque StringBuilder. Si se trata de datos binarios, use una matriz de bytes en su lugar, pero no use el método inseguro. Si bien el temor exagerado de "inseguro" que se está volviendo común es un poco tonto y no hay ninguna razón para no usarlo cuando lo justifique, en este caso es innecesario. Uso:

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

Depende del costo de clasificación. Si realiza una gran cantidad de clasificación o los datos que se organizan son grandes, es posible que desee reutilizar el búfer en lugar de crear/destruir el búfer del generador de cadenas cada vez.

Cuestiones relacionadas