从C#调用非托管函数:我应该传递StringBuilder还是使用不安全的代码?

我有一个C#程序需要将char缓冲区传递给非托管函数。 我发现两种似乎可靠的方法,但我不确定我应该选择哪种方式。

这是非托管函数的签名。

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

第一个选项是将缓冲区定义为StringBuilder,如下所示。

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

这很简单,它可以工作,我想我基本上理解为什么它可以工作,因为StringBuilder在幕后有一个缓冲区,所以(我假设)interop层只是将StringBuilder编组为char *。

另一种选择是使用不安全的代码。

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

第二种方法是更多的代码,但它也更明确地说明了正在发生的事情。

这两种方法基本上是一样的吗? 有没有理由选择一个而不是另一个?

我非常喜欢使用StringBuilder版本。

这两者之间不会有太大的差别,使用不安全的代码并不是那么干净。

在我看来,由于有一种方法可以使用核心库类来解决问题,因此使用不安全的代码而没有明确(并且需要)的好处是过早的优化。

虽然使用StringBuilder是首选,但有一点需要注意。 例如,想象一下,在你的getNextResponse方法中,你将指针存储到某个静态变量并在另一个方法中使用它:

 char* globalPointer; int getNextResponse(char *buffer) { globalPointer = buffer; return 0; } void someOtherMethod() { printf("%s\n", globalPointer); } 

现在让我们看一下管理方面:

 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. 

不安全的方法可以保证您不会移动内存位置:

 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 } 

在这种情况下,不安全版本将更好地工作。

虽然我无法确定权衡,但我可以分享自己的经验。 我专门使用了StringBuilder方法并且没有遇到任何问题。 我喜欢它的简单代码和避免不安全。

这取决于编组的成本。 如果进行大量编组或编组的数据很大,则可能需要重用缓冲区,而不是每次都创建/销毁字符串构建器缓冲区。

这取决于缓冲区中的数据实际是什么。 如果是字符数据,请使用StringBuilder方法。 如果是二进制数据,则使用字节数组,但不要使用不安全方法。 虽然夸大的“不安全”的恐惧变得有点愚蠢,但没有理由不在有保证的情况下使用它,在这种情况下它是没有必要的。 使用:

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