簡體   English   中英

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

[英]String Concatenation unsafe in C#, need to use StringBuilder?

我的問題是: C#中的字符串連接是否安全? 如果字符串連接導致意外錯誤,並且使用StringBuilder替換該字符串連接會導致這些錯誤消失,那可能表示什么?

背景:我正在開發一個小命令行C#應用程序。 它接受命令行參數,執行稍微復雜的SQL查詢,並將大約1300行數據輸出到格式化的XML文件中。

我的初始程序總是在調試模式下正常運行。 但是,在發布模式下,它將獲得大約第750個SQL結果,然后因錯誤而死亡。 錯誤是無法讀取某一列數據,即使通過SqlDataReader對象的Read()方法剛剛返回true也是如此。

通過對代碼中的所有操作使用StringBuilder來修復此問題,之前已經存在“string1 + string2”。 我不是在談論SQL查詢循環中的字符串連接,其中StringBuilder已經在使用中。 我在談論代碼中較早的兩個或三個短字符串變量之間的簡單連接。

我的印象是C#足夠聰明,可以通過添加幾個字符串來處理內存管理。 我錯了嗎? 或者這是否表明其他一些代碼問題?

回答你的問題: 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";
// ...

雖然所有這些隱式臨時變量幾乎都可以立即進行垃圾收集,但仍然必須進行分配。 在緊密循環中執行連接時,這會給垃圾收集器帶來很大的壓力,如果不出意外,會使代碼運行得非常慢。 我已經看到了這個第一手的性能影響,隨着你的連接字符串變大,它變得非常引人注目。

如果您執行的不僅僅是幾個字符串連接,建議的方法是始終使用StringBuilder StringBuilder使用可變緩沖區來減少構建字符串所需的分配數。

如果在循環中連接大量字符串,則字符串連接比使用StringBuilder更加內存密集。 在極端情況下,你可能會耗盡內存。

這幾乎肯定是代碼中的一個錯誤。

也許你正在連接大量的字符串。 或者也許是完全不同的東西。

我會在沒有任何先入為主的原因的情況下重新進行調試 - 如果您仍然遇到問題,請嘗試將其降低到重現問題和發布代碼所需的最低限度。

除了你正在做的事情可能最好用XML API代替字符串或StringBuilder我懷疑你看到的錯誤是由字符串連接引起的。 也許切換到StringBuilder只是掩蓋了錯誤或優雅地過了它,但我懷疑使用字符串真的是原因。

連接版本與字符串生成器版本需要多長時間? 您與DB的連接可能已關閉。 如果你正在進行大量的連接,我會使用StringBuilder,因為它更有效率。

一個原因可能是字符串在.Net中是不可變的,因此當您對連接等操作進行操作時,實際上是在創建一個新字符串。

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

在任何一種情況下,對於這種類型的操作,StringBuilder優於“string1 + string2”。 雖然,使用內置的XML功能會更好。

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代碼,你會看到底層發生了什么。

這是我在黑暗中的鏡頭......

.NET中的字符串(不是stringbuilders)進入String Intern Pool。 這基本上是由CLR管理的區域,用於共享字符串以提高性能。 這里必須有一些限制,雖然我不知道這個限制是什么。 我想你正在做的所有連接都是撞到字符串實習池的天花板。 所以SQL說是的我有一個值,但它不能把它放在任何地方,所以你得到一個例外。

一個快速簡便的測試將是NGEN程序集,看看你是否仍然得到錯誤。 在nGen'ing之后,您的應用程序將不再使用該池。

如果失敗了,我會聯系微軟嘗試獲取一些詳細信息。 我認為我的想法聽起來似乎有道理,但我不知道為什么它在調試模式下工作。 也許在調試模式下,字符串不會被實現。 我也不是專家。

將字符串復合在一起時,我總是使用StringBuilder。 它是專為它而設計的,只需使用“string1 + string2”就更有效率。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM