回答您的问题:
C#(和一般的 .NET)中的字符串连接是“安全的”,但是如您所描述的那样在紧密循环中执行它可能会导致严重的内存压力并对垃圾收集器造成压力。
我会大胆猜测您所说的错误与某种资源耗尽有关,但如果您能提供更多详细信息会有所帮助——例如,您是否收到异常?应用程序是否异常终止?
背景:
.NET 字符串是不可变的,所以当你进行这样的连接时:
var stringList = new List<string> {"aaa", "bbb", "ccc", "ddd", //... };
string result = String.Empty;
foreach (var s in stringList)
{
result = result + s;
}
这大致相当于以下内容:
string result = "";
result = "aaa"
string temp1 = result + "bbb";
result = temp1;
string temp2 = temp1 + "ccc";
result = temp2;
string temp3 = temp2 + "ddd";
result = temp3;
// ...
result = tempN + x;
这个例子的目的是强调每次循环都会导致分配一个新的临时字符串。
由于字符串是不可变的,因此运行时没有其他选择,只能在每次将另一个字符串添加到结果末尾时分配一个新字符串。
尽管result
字符串会不断更新以指向最新和最大的中间结果,但您会产生大量这些未命名的临时字符串,这些字符串几乎立即可以进行垃圾回收。
在此连接结束时,您将在内存中存储以下字符串(为简单起见,假设垃圾收集器尚未运行)。
string a = "aaa";
string b = "bbb";
string c = "ccc";
// ...
string temp1 = "aaabbb";
string temp2 = "aaabbbccc";
string temp3 = "aaabbbcccddd";
string temp4 = "aaabbbcccdddeee";
string temp5 = "aaabbbcccdddeeefff";
string temp6 = "aaabbbcccdddeeefffggg";
// ...
尽管所有这些隐式临时变量几乎可以立即进行垃圾回收,但它们仍然必须被分配。当在一个紧密的循环中执行连接时,这会给垃圾收集器带来很大的压力,如果没有别的,会让你的代码运行得很慢。我亲眼目睹了这对性能的影响,并且随着您的连接字符串变得更大,它变得真正具有戏剧性。
推荐的方法是始终使用 aStringBuilder
如果您正在执行多个字符串连接。 StringBuilder
使用可变缓冲区来减少构建字符串所需的分配数量。