5

私の質問はこれです: C#での文字列の連結は安全ですか?文字列の連結によって予期しないエラーが発生し、StringBuilderを使用してその文字列の連結を置き換えると、それらのエラーが消える場合、それは何を示しているのでしょうか。

背景:私は小さなコマンドラインC#アプリケーションを開発しています。コマンドライン引数を取り、少し複雑なSQLクエリを実行し、約1300行のデータをフォーマットされたXMLファイルに出力します。

私の最初のプログラムは、デバッグモードでは常に正常に実行されます。ただし、リリースモードでは、約750番目のSQL結果に到達し、エラーで終了します。エラーは、SqlDataReaderオブジェクトのRead()メソッドがtrueを返しただけでも、データの特定の列を読み取ることができなかったことです。

この問題は、以前は「string1+string2」があったコード内のすべての操作にStringBuilderを使用することで修正されました。StringBuilderがすでに使用されているSQLクエリループ内の文字列連結については話していません。コードの前半で、2つまたは3つの短い文字列変数間の単純な連結について話しています。

C#は、いくつかの文字列を一緒に追加するためのメモリ管理を処理するのに十分スマートであるという印象を受けました。私が間違っている?それとも、これは他の種類のコードの問題を示していますか?

4

8 に答える 8

15

あなたの質問に答えるには: 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複数の文字列連結を行う場合は、常に a を使用することをお勧めします。 StringBuilder変更可能なバッファを使用して、文字列を構築するために必要な割り当ての数を減らします。

于 2009-04-22T21:32:09.737 に答える
11

ループ内に多数の文字列を含む場合は、StringBuilderを使用するよりもメモリを大量に消費しますが、文字列の連結は安全です。また、極端な場合、メモリが不足する可能性があります。

それはほぼ間違いなくあなたのコードのバグです。

たぶん、あなたは非常に多くの文字列を汚染しているのでしょう。あるいは、まったく別の何かかもしれません。

根本的な原因を先取りせずにデバッグに戻ります。それでも問題が解決しない場合は、問題を再現してコードを投稿するために必要な最小限に抑えてください。

于 2009-04-22T20:29:07.687 に答える
7

あなたがしていることは別として、おそらく文字列やStringBuilderの代わりにXML APIを使用するのが最善でしょう。表示されるエラーは、文字列の連結によるものではないかと思います。StringBuilderに切り替えると、エラーがマスクされたり、正常に処理されたりした可能性がありますが、文字列を使用したことが本当に原因ではないかと思います。

于 2009-04-22T20:29:45.423 に答える
3

連結バージョンと文字列ビルダーバージョンの比較にはどのくらい時間がかかりますか?DBへの接続が閉じられている可能性があります。多くの連結を行う場合は、もう少し効率的であるため、StringBuilderを使用します。

于 2009-04-22T20:30:28.543 に答える
1

原因の 1 つは、.Net では文字列が不変であるため、連結などの操作を行うと、実際には新しい文字列を作成していることです。

別の考えられる原因は、文字列の長さが int であるため、可能な最大長が Int32.MaxValue または 2,147,483,647 であることです。

いずれの場合も、このタイプの操作では、「string1 + string2」よりも StringBuilder の方が適しています。ただし、組み込みの XML 機能を使用するとさらに効果的です。

于 2009-04-22T20:35:50.650 に答える
1

string.Concat(string[])文字列を連結する最も速い方法です。StringBuilderループで使用すると、特にStringBuilder各反復でを作成すると、パフォーマンスが少し低下します。「c# string format vs stringbuilder」などをグーグルで検索すると、参照がたくさんあります。 http://www.codeproject.com/KB/cs/StringBuilder_vs_String.aspxは、時間についてのアイデアを提供します。ここで string.Join は連結テストに勝ちますが、これはstring.Concat(string, string)、配列を取るオーバーロードされたバージョンの代わりに が使用されているためだと思います。さまざまな方法で生成された MSIL コードを見ると、内部で何が起こっているかがわかります。

于 2009-04-23T12:32:32.287 に答える
0

これが暗闇での私のショットです...

.NET の文字列 (stringbuilders ではない) は、String Intern プールに入ります。これは基本的に、パフォーマンスを向上させるために文字列を共有するために CLR によって管理される領域です。ここには何らかの制限が必要ですが、その制限が何であるかはわかりません。あなたが行っているすべての連結は、ストリングインターンプールの天井にぶつかっていると思います。したがって、SQL は「はい、私はあなたのために値を持っていますが、それをどこにも置くことができないため、例外が発生します」と言います。

迅速かつ簡単なテストは、アセンブリをnGenして、それでもエラーが発生するかどうかを確認することです。nGen の後、アプリケーションはプールを使用しなくなります。

それが失敗した場合は、Microsoft に連絡して詳細を確認します。私の考えはもっともらしく聞こえると思いますが、なぜデバッグモードで機能するのかわかりません。おそらく、デバッグ モードでは文字列はインターンされません。私も専門家ではありません。

于 2009-04-22T20:50:57.573 に答える
-3

文字列を組み合わせるときは、常にStringBuilderを使用します。それはそれのために設計されており、単に「string1+string2」を使用するよりも効率的です。

于 2009-04-22T20:29:13.007 に答える