如果不安全代码是您的应用程序的可行替代方案,那么您可以重写字符串的内容和长度。这将允许您拥有一个可以预分配的可重用字符串池,从而避免垃圾收集。
AC# 字符串在内存中的布局如下:
int Capacity;
int Length;
char FirstCharacter;
// remaining characters follow
字符数据以空值结尾(便于与非托管 C/C++ 代码进行互操作),并且还存储当前长度和最大容量,以避免那些讨厌的缓冲区溢出问题。
以下是如何在不分配任何新内存的情况下将新内容注入现有字符串:
static unsafe void RecycleString(string s, char[] newcontents)
{
// First, fix the string so the GC doesn't move it around on us, and get a pointer to the character data.
fixed (char* ps = s)
{
// We need an integer pointer as well, to check capacity and update length.
int* psi = (int*)ps;
int capacity = psi[-2];
// Don't overrun the buffer!
System.Diagnostics.Debug.Assert(capacity > newcontents.Length);
if (capacity > newcontents.Length)
{
for (int i = 0; i < newcontents.Length; ++i)
{
ps[i] = newcontents[i];
}
// Add null terminator and update length accordingly.
ps[newcontents.Length] = '\0';
psi[-1] = newcontents.Length;
}
}
}
有了它,您可以回收并重新解析相同的字符串以符合您的心意。这是一个简单的示例来演示:
private static void ReusableStringTest()
{
char[] intFromWire = new char[] { '9', '0', '0', '0' };
char[] floatFromWire = new char[] { '3', '.', '1', '4', '1', '5' };
string reusableBuffer = new string('\0', 128);
RecycleString(reusableBuffer, intFromWire);
int i = Int32.Parse(reusableBuffer);
Console.WriteLine("Parsed integer {0}", i);
RecycleString(reusableBuffer, floatFromWire);
float f = Single.Parse(reusableBuffer);
Console.WriteLine("Parsed float {0}", f);
}
生成的输出正如人们所希望的那样:
解析整数 9000
解析浮点数 3.1415
如果不安全的代码让你感到紧张,请记住我们用 C 和 C++ 编程的那些年,那时一切都是不安全的!