字符串连接在C#中不安全,需要使用StringBuilder吗?

我的问题是: C#中的字符串连接是否安全? 如果字符串连接导致意外错误,并且使用StringBuilder替换该字符串连接会导致这些错误消失,那可能表示什么?

背景:我正在开发一个小命令行C#应用程序。 它接受命令行参数,执行稍微复杂的SQL查询,并将大约1300行数据输出到格式化的XML文件中。

我的初始程序总是在调试模式下正常运行。 但是,在发布模式下,它将获得大约第750个SQL结果,然后因错误而死亡。 错误是无法读取某一列数据,即使通过SqlDataReader对象的Read()方法刚刚返回true也是如此。

通过对代码中的所有操作使用StringBuilder来解决此问题,之前已经存在“string1 + string2”。 我不是在谈论SQL查询循环中的字符串连接,其中StringBuilder已经在使用中。 我在谈论代码中较早的两个或三个短字符串变量之间的简单连接。

我的印象是C#足够聪明,可以通过添加几个字符串来处理内存管理。 我错了吗? 或者这是否表明其他一些代码问题?

除了你正在做的事情可能最好用XML API代替字符串或StringBuilder我怀疑你看到的错误是由于字符串连接。 也许切换到StringBuilder只是掩盖了错误或优雅地过了它,但我怀疑使用字符串真的是原因。

回答你的问题: C#中的字符串连接(以及一般的.NET) “安全的”,但是如你所描述的那样在紧密循环中执行它可能会导致严重的内存压力并对垃圾收集器造成压力。

我猜想你所说的错误与某种资源耗尽有关,但如果你能提供更多详细信息会有所帮助 – 例如,你收到了例外吗? 应用程序是否exception终止?

背景: .NET字符串是不可变的,所以当你进行这样的连接时:

var stringList = new List {"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"; // ... 

虽然所有这些隐式临时变量几乎都可以立即进行垃圾收集,但仍然必须进行分配。 在紧密循环中执行连接时,这会给垃圾收集器带来很大的压力,如果没有其他的话,会使代码运行得非常慢。 我已经看到了这个第一手的性能影响,随着你的连接字符串变大,它变得非常引人注目。

建议的方法是,如果您执行的不仅仅是几个字符串连接,请始终使用StringBuilder StringBuilder使用可变缓冲区来减少构建字符串所需的分配数。

如果在循环中连接大量字符串,则字符串连接比使用StringBuilder更加内存密集。 在极端情况下,你可能会耗尽内存。

这几乎肯定是代码中的一个错误。

也许你正在连接大量的字符串。 或者也许是完全不同的东西。

我会在没有任何先入为主的原因的情况下重新进行调试 – 如果您仍然遇到问题,请尝试将其降低到重现问题和发布代码所需的最低限度。

连接版本与字符串生成器版本需要多长时间? 您与DB的连接可能已关闭。 如果你正在进行大量的连接,我会使用StringBuilder,因为它更有效率。

一个原因可能是字符串在.Net中是不可变的,因此当您对连接等操作进行操作时,实际上是在创建一个新字符串。

另一个可能的原因是字符串长度是一个int,因此最大可能的长度是Int32.MaxValue或2,147,483,647。

在任何一种情况下,对于这种类型的操作,StringBuilder优于“string1 + string2”。 虽然,使用内置的XMLfunction会更好。

这是我在黑暗中的镜头……

.NET中的字符串(不是stringbuilders)进入String Intern Pool。 这基本上是由CLR管理的区域,用于共享字符串以提高性能。 这里必须有一些限制,虽然我不知道这个限制是什么。 我想你正在做的所有串联都是撞到字符串实习池的天花板。 所以SQL说是的,我有一个值,但它不能把它放在任何地方,所以你得到一个例外。

一个快速简单的测试是nGen你的组件,看看你是否仍然得到错误。 在nGen’ing之后,您的应用程序将不再使用该池。

如果失败了,我会联系微软尝试获取一些详细信息。 我认为我的想法听起来似乎有道理,但我不知道为什么它在调试模式下工作。 也许在调试模式下,字符串不会被实现。 我也不是专家。

string.Concat(string[])是迄今为止连接字符串的最快方法。 当在循环中使用时,它会在性能上轻易杀死StringBuilder ,尤其是在每次迭代中创建StringBuilder时。 如果您使用Google“c#string format vs stringbuilder”或类似内容,则会有大量引用。 http://www.codeproject.com/KB/cs/StringBuilder_vs_String.aspx为您提供了有关时代的理念。 这里string.Join赢得了连接测试,但我相信这是因为使用了string.Concat(string, string)而不是带有数组的重载版本。 如果你看看由不同方法生成的MSIL代码,你会看到底层发生了什么。

将字符串复合在一起时,我总是使用StringBuilder。 它是专为它而设计的,只需使用“string1 + string2”就更有效率。