2009-04-22 22 views
5

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

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

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

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

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

+0

StringBuilderバージョンに対して、文字列連結コードを貼り付けることはできますか?文字列連結とStringBuilderとの違いを得ることができると思う唯一の理由は、いくつかのもののオーバーロードが呼び出されますが、それは記述しているような効果はありません。 –

+0

オリジナルコード: string filepath = path + fileroot + ".xml"; 更新されたコード: string filepath = new StringBuilder(パス) .Append(fileroot) .Append( "。xml")。ToString(); 私はいくつかの場所でそれを変更しました。これらはすべてメインループの前です。メインループの中で、私は常にStringBuilderを使用してXMLファイルの内容を構築してきました。 (私はXML APIを使用していません。なぜなら、XML APIの方が早かったからです。これは、すばやく汚れたプログラムになるはずです。) –

答えて

7

あなたがやっていることは、文字列やStringBuilderの代わりにXML APIを使用するのが最も良いのですが、エラーは文字列連結によるものだとは思っていません。たぶん、StringBuilderに切り替えるだけでエラーがマスクされたり、正常に処理されたりしましたが、文字列を使用することが本当に原因でした。

+0

ありがとうございますあなたの反応のために。私はまだ私のすべての変更の後に特定のデータセットのエラーを見終わった。私は問題の本当の根本はSQL接続にあったと思っています。そして、ヨハネスが言ったように、 "StringBuilderへの切り替えはエラーを隠しただけです"。 別のプロジェクトのSQLラッパークラスを使用して問題を解決しました。このクラスはSQL結果セット全体をDictionaryオブジェクトに変換するので、結果セットとSQL接続を開いたままにする必要はありません。 –

11

ループ内の多数の文字列を連結する場合、StringBuilderを使用するよりもメモリ集約的ですが、文字列連結は安全です。極端な場合は、メモリが不足している可能性があります。

ほとんどの場合、コード内にバグがあります。

多分、非常に多数の文字列を連結しているかもしれません。それとも、まったく別のものかもしれません。

私は根本的な根本的な根拠がなくてもデバッグに戻ります。まだ問題が残っている場合は、問題を再現してコードを投稿するのに必要な最小限に抑えてください。

-3

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

+3

不正なアドバイス。 string1 + string2は、多くの状況でStringBuilderより高速です。ループ内で多数の連結を実行すると、StringBuilderが勝ちます。 – Joe

+0

私にお知らせいただきありがとうございます。私は「C#via CLR」のほうが速いと読みましたが、そうでない場合は知っておきましょう。 – patjbs

+0

私はいくつかのテストを実行しただけで、 "str1 + str2"を介して2つの短い文字列を連結しても、StringBuilderを利用するよりも実際には遅くなっています。そして私が読んだことから得たいくつかの優れたブログ記事は、その結論に同意する傾向があります。 str1 + str2を使用することは便利ですが、小さな文字列の連結には大きなパフォーマンス上の影響はありませんが、StringBuilderは確かに悪くなく、コンティニューの数が増えるにつれて勝ちます(わずか4の違いがsigです) – patjbs

3

連結バージョンと文字列ビルダーのバージョンはどのくらいかかりますか? DBへの接続が閉じられている可能性があります。あなたが多くの連結をしているなら、私はStringBuilderを使うことになるでしょう。

1

文字列が.Netで不変である可能性があります。連結などの操作を行うと、実際には新しい文字列が作成されます。

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

どちらの場合でも、このタイプの操作ではStringBuilderが "string1 + string2"より優れています。ただし、組み込みのXML機能を使用する方がさらに優れています。

0

はここに暗闇の中で私のショット... .NETで

文字列(ないstringbuilders)文字列インターンプールに入っています。これは基本的に、パフォーマンスを向上させるために文字列を共有するCLRによって管理される領域です。その限界が何であるか分かりませんが、ここにはある程度の限界があります。あなたがやっているすべての連結が、文字列のインターンプールの天井に当たっていると想像します。だからSQLははい、私はあなたのための価値があると言いますが、どこにでも置くことができないため、例外が発生します。

あなたのアセンブリをnGenにすばやく簡単にテストして、まだエラーが発生するかどうかを確認してください。 nGenされた後、アプリケーションはもはやプールを使用しません。

これが失敗した場合は、マイクロソフトに連絡して詳細を確認してください。私のアイデアは納得のいくものだと思うが、なぜデバッグモードで動作するのか分からない。おそらく、デバッグモードでは、文字列はインターンされません。私は専門家もいません。あなたの質問に答えるために

13

:(一般的におよび.NET)C#で 文字列contatenationをは「安全」ですが、あなたが説明するようにタイトなループでそれを行うと、深刻なメモリ不足が発生し、負担をかける可能性がありますガベージコレクタで

あなたが話すエラーは何らかのリソースの消耗に関連していると推測されますが、例外を受け取ったかどうかなど詳細を提供できると便利です。アプリケーションが異常終了しましたか?

背景: .NET文字列は不変ですので、あなたはこのような連結行うとき:

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; 

この目的を:

var stringList = new List<string> {"aaa", "bbb", "ccc", "ddd", //... }; 
string result = String.Empty; 
foreach (var s in stringList) 
{ 
    result = result + s; 
} 

を。これは、次のとほぼ同じです例えば、ループの周りに新しい一時的な文字列が割り当てられることを強調することです。

文字列が不変なので、ランタイムには代替オプションはありませんが、結果の最後に別の文字列を追加するたびに新しい文字列を割り当てることができます。

文字列は常に最新かつ最高の中間結果を指すように更新されますが、すぐにガベージコレクションの対象となるこれらの名前のない一時的な文字列が多数生成されています。

この連結の最後に、メモリに以下の文字列が格納されます(簡単にするため、ガベージコレクタはまだ実行されていないものとします)。

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は可変バッファを使用して、文字列を構築するのに必要な割り当ての数を減らします。

+0

優秀な答え:) – patjbs

+4

私は確かにa + b + c + d ...一つのステートメントで中間の文字列を生成しません。 String.Concatのように動作します。 – Joe

+0

Joe:あなたは大丈夫です。私は答えを簡素化するにはあまりにも遠くに行きました。私はループを追加して、事実上より正確になるようにしました。それは良いですか? –

0

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コードを見れば、ボンネットの下に何が起こっているのかが分かります。

関連する問題