文字列出力:C# でフォーマットまたは連結しますか?
-
08-06-2019 - |
質問
文字列を出力または連結したいとします。次のスタイルのうちどれが好みですか?
var p = new { FirstName = "Bill", LastName = "Gates" };
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
Console.WriteLine(p.FirstName + " " + p.LastName);
フォーマットを使用しますか、それとも単純に文字列を連結しますか?あなたのお気に入りは?これらのいずれかがあなたの目を痛めていますか?
一方を使用し、他方を使用しない合理的な議論はありますか?
2番目に行きます。
解決
このコードを試してください。
これはコードを少し変更したバージョンです。
1.おそらく測定しようとしているものよりも数桁遅いため、Console.WriteLine を削除しました。
2.ループの前にストップウォッチを開始し、ループの直後に停止します。こうすることで、たとえば関数の実行に 26.4 ティックかかる場合でも精度が失われることはありません。
3.結果をいくつかの反復で分割する方法が間違っていました。1000 ミリ秒と 100 ミリ秒の場合に何が起こるかを見てください。どちらの状況でも、1000000 で割ると 0 ミリ秒になります。
Stopwatch s = new Stopwatch();
var p = new { FirstName = "Bill", LastName = "Gates" };
int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;
string result;
s.Start();
for (var i = 0; i < n; i++)
result = (p.FirstName + " " + p.LastName);
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();
s.Start();
for (var i = 0; i < n; i++)
result = string.Format("{0} {1}", p.FirstName, p.LastName);
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();
Console.Clear();
Console.WriteLine(n.ToString()+" x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Thread.Sleep(4000);
これらは私の結果です:
1000000 x 結果 = string.Format("{0} {1}", p.FirstName, p.LastName);取った:618ms - 2213706 ティック
1000000 x 結果 = (p.名 + " " + p.姓);取った:166ms - 595610ティック
他のヒント
最も速く実行されるコードをすぐに見つけようとする人が非常に多いことに驚きました。 100 万回の反復の処理時間がまだ 1 秒未満である場合、これはエンド ユーザーにとって何らかの形で認識されるでしょうか?あまり可能性は高くありません。
時期尚早な最適化 = 失敗。
私は一緒に行きます String.Format
このオプションは、アーキテクチャの観点から最も合理的であるという理由だけで選択されます。問題になるまでは、パフォーマンスについては気にしません (問題になった場合は、次のように自問します。一度に 100 万個の名前を連結する必要がありますか?さすがに画面に入りきらないですね…)
顧客が後で表示するかどうかを設定できるように変更したいかどうかを検討してください。 "Firstname Lastname"
または "Lastname, Firstname."
[形式] オプションを使用すると、形式文字列を置き換えるだけで簡単になります。concat を使用する場合は、追加のコードが必要になります。確かに、この特定の例では大したことではないように思えますが、推測してみてください。
ああ、他の返信のいずれかを読んだ後、操作の順序を逆にしてみました。つまり、最初に連結を実行し、次に String.Format...
Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 8ms - 30488 ticks
Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 0ms - 182 ticks
したがって、操作の順序によって大きな違いが生じます。むしろ、最初の操作は常にはるかに遅くなります。
以下は、操作が複数回完了した実行の結果です。順序を変更してみましたが、最初の結果が無視されれば、通常は同じルールに従います。
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 5ms - 20335 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 156 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 122 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 181 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 122 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 142 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 117 ticks
ご覧のとおり、同じメソッドの後続の実行 (コードを 3 つのメソッドにリファクタリングしました) は徐々に高速化しています。最も速いのは Console.WriteLine(String.Concat(...)) メソッドのようで、次に通常の連結、次にフォーマットされた操作が続きます。
最初の操作が実行される前に Console.Writeline("Start!") を配置すると、常にラインに戻るため、起動の最初の遅延はおそらく Console Stream の初期化によるものと考えられます。
文字列は不変です。これは、コード内で同じ小さなメモリが繰り返し使用されることを意味します。同じ 2 つの文字列を加算したり、同じ新しい文字列を繰り返し作成したりしても、メモリには影響しません。.Net は、同じメモリ参照を使用するだけでも十分賢いのです。したがって、コードは 2 つの concat メソッド間の違いを実際にはテストしません。
サイズについてはこれを試着してください:
Stopwatch s = new Stopwatch();
int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0, sbElapsedMilliseconds = 0, sbElapsedTicks = 0;
Random random = new Random(DateTime.Now.Millisecond);
string result;
s.Start();
for (var i = 0; i < n; i++)
result = (random.Next().ToString() + " " + random.Next().ToString());
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();
s.Start();
for (var i = 0; i < n; i++)
result = string.Format("{0} {1}", random.Next().ToString(), random.Next().ToString());
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();
StringBuilder sb = new StringBuilder();
s.Start();
for(var i = 0; i < n; i++){
sb.Clear();
sb.Append(random.Next().ToString());
sb.Append(" ");
sb.Append(random.Next().ToString());
result = sb.ToString();
}
s.Stop();
sbElapsedMilliseconds = s.ElapsedMilliseconds;
sbElapsedTicks = s.ElapsedTicks;
s.Reset();
Console.WriteLine(n.ToString() + " x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(\" \"); sb.Append(random.Next().ToString()); result = sb.ToString(); took: " + (sbElapsedMilliseconds) + "ms - " + (sbElapsedTicks) + " ticks");
Console.WriteLine("****************");
Console.WriteLine("Press Enter to Quit");
Console.ReadLine();
サンプル出力:
1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 513ms - 1499816 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 393ms - 1150148 ticks
1000000 x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(" "); sb.Append(random.Next().ToString()); result = sb.ToString(); took: 405ms - 1185816 ticks
翻訳者が残念だ
もし、あんたが 知る アプリケーションは英語のままになりますが、問題なく、時間を節約できます。ただし、多くの文化では通常、住所などに姓、名が表示されます。
それで使用してください string.Format()
, 特に、英語が第一言語ではない場所でアプリケーションを使用する予定がある場合はそうです。
100,000 回繰り返した結果は次のとおりです。
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took (avg): 0ms - 689 ticks
Console.WriteLine(p.FirstName + " " + p.LastName); took (avg): 0ms - 683 ticks
そして、ベンチコードは次のとおりです。
Stopwatch s = new Stopwatch();
var p = new { FirstName = "Bill", LastName = "Gates" };
//First print to remove the initial cost
Console.WriteLine(p.FirstName + " " + p.LastName);
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
int n = 100000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;
for (var i = 0; i < n; i++)
{
s.Start();
Console.WriteLine(p.FirstName + " " + p.LastName);
s.Stop();
cElapsedMilliseconds += s.ElapsedMilliseconds;
cElapsedTicks += s.ElapsedTicks;
s.Reset();
s.Start();
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
s.Stop();
fElapsedMilliseconds += s.ElapsedMilliseconds;
fElapsedTicks += s.ElapsedTicks;
s.Reset();
}
Console.Clear();
Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took (avg): " + (fElapsedMilliseconds / n) + "ms - " + (fElapsedTicks / n) + " ticks");
Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took (avg): " + (cElapsedMilliseconds / n) + "ms - " + (cElapsedTicks / n) + " ticks");
したがって、誰の返信を回答としてマークすればよいのかわかりません:)
文字列の連結は、このような単純なシナリオでは問題ありませんが、それよりも複雑なシナリオ (LastName、FirstName であっても) ではさらに複雑になります。この形式を使用すると、コードを読んだときに文字列の最終的な構造がどのようになるか一目でわかりますが、連結では、最終結果をすぐに識別することはほとんど不可能になります (このような非常に単純な例を除く)。
これが意味するのは、長期的には、文字列フォーマットを変更するために戻ってきたときに、すぐに立ち寄ってフォーマット文字列をいくつか調整するか、眉間にシワを寄せてあちこち動き回るかのどちらかになるということです。種類のプロパティ アクセサーがテキストと混在しているため、問題が発生する可能性が高くなります。
.NET 3.5 を使用している場合は、拡張メソッドを使用できます このように 次のような、簡単に流れるような、端的な構文を取得します。
string str = "{0} {1} is my friend. {3}, {2} is my boss.".FormatWith(prop1,prop2,prop3,prop4);
最後に、アプリケーションが複雑になるにつれて、アプリケーション内の文字列を適切に維持するために、文字列をリソース ファイルに移動してローカライズするか、単に静的ヘルパーに移動するかを決定する場合があります。一貫してフォーマットを使用している場合、これは非常に簡単に達成できます。また、コードを次のようなものを使用するように非常に簡単にリファクタリングできます。
string name = String.Format(ApplicationStrings.General.InformalUserNameFormat,this.FirstName,this.LastName);
非常に単純な操作の場合は連結を使用しますが、要素が 2 つまたは 3 つを超えると、IMO では Format の方が適切になります。
String.Format を好むもう 1 つの理由は、.NET 文字列は不変であり、この方法で作成すると一時/中間コピーの作成が少なくなることです。
私はスタイルの好みを完全に理解しており、最初の回答で連結を選択したのは部分的には私自身の好みに基づいていますが、私の決定の一部は連結の方が高速であるという考えに基づいていました。そこで、好奇心からテストしてみたところ、特にこのような小さな文字列の場合、驚くべき結果が得られました。
次のコードを使用します。
System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
var p = new { FirstName = "Bill", LastName = "Gates" };
s.Start();
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
s.Stop();
Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");
s.Reset();
s.Start();
Console.WriteLine(p.FirstName + " " + p.LastName);
s.Stop();
Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");
次の結果が得られました。
Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 2ms - 7280 ticks
Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 0ms - 67 ticks
フォーマット方法を使用すると、100 倍以上遅くなります。連結は 1ms としても記録されなかったので、タイマー ティックも出力しました。
基本的な文字列連結の場合、私は通常、読みやすく単純な 2 番目のスタイルを使用します。ただし、より複雑な文字列の組み合わせを行う場合は、通常 String.Format を選択します。
String.Format は多くの引用符とプラスを節約します...
Console.WriteLine("User {0} accessed {1} on {2}.", user.Name, fileName, timestamp);
vs
Console.WriteLine("User " + user.Name + " accessed " + fileName + " on " + timestamp + ".");
保存された文字はほんのわずかですが、この例では、形式を変更することでかなりきれいになると思います。
より良いテストは、Perfmon と CLR メモリ カウンターを使用してメモリを監視することです。私の理解では、単に文字列を連結する代わりに String.Format を使用したい理由は、文字列が不変であるため、次のパスで再利用する必要がある一時文字列でガベージ コレクターに不必要に負担をかけることであるということです。
StringBuilder と String.Format は、速度が遅くなる可能性がありますが、メモリ効率が高くなります。
特に文字列が長くなると読みやすくなるため、一般的には前者を好みます。
もう 1 つの利点は、パフォーマンスの 1 つであると思います。後者は、最終文字列を Console.Write メソッドに渡す前に、実際に 2 つの文字列作成ステートメントを実行します。String.Format は内部で StringBuilder を使用しているため、複数の連結が回避されていると思います。
ただし、String.Format (および Console.Write などの他の同様のメソッド) に渡すパラメーターが値型の場合、それらは渡される前にボックス化され、それ自体がパフォーマンスに影響を与える可能性があることに注意してください。 これに関するブログ投稿はこちら.
今から 1 週間後の 2015 年 8 月 19 日、この質問はちょうど 7 年前になります。これを行うためのより良い方法が登場しました。 より良い 文字列を連結するだけのパフォーマンス テストを行っていないため、保守性の点で (しかし、最近ではそれが問題になるでしょうか?)数ミリ秒の差?)。新しい方法 C#6.0:
var p = new { FirstName = "Bill", LastName = "Gates" };
var fullname = $"{p.FirstName} {p.LastName}";
この新機能は、 より良い, 、IMO、および 私たちの場合は実際にはもっと良いです 値がいくつかの要因に依存するクエリ文字列を構築するコードがあるためです。引数が 6 つある 1 つのクエリ文字列を想像してください。したがって、次のようにする代わりに、次のようにします。
var qs = string.Format("q1={0}&q2={1}&q3={2}&q4={3}&q5={4}&q6={5}",
someVar, anotherVarWithLongName, var3, var4, var5, var6)
in は次のように書くことができ、読みやすくなります。
var qs=$"q1={someVar}&q2={anotherVarWithLongName}&q3={var3}&q4={var4}&q5={var5}&q6={var6}";
C# 6.0 以降 補間された文字列 を使用してこれを行うことができ、形式がさらに簡素化されます。
var name = "Bill";
var surname = "Gates";
MessageBox.Show($"Welcome to the show, {name} {surname}!");
補間された文字列式は、式を含むテンプレート文字列のように見えます。補間文字列式は、含まれる式を式の結果の ToString 表現で置き換えることによって文字列を作成します。
補間文字列のパフォーマンスは String.Format と同様ですが、値と式がインラインで挿入されるため、読みやすさが向上し、構文が短くなります。
も参照してください この dotnetperls の記事 文字列補間について。
文字列をフォーマットするデフォルトの方法を探している場合、これは読みやすさとパフォーマンスの点で理にかなっています (特定の使用例でマイクロ秒が違いを生む場合を除く)。
- フォーマットは「.NET」の方法です。特定のリファクタリング ツール (Refactor!1 つ目は、書式設定スタイルを使用するために concat スタイルのコードをリファクタリングすることさえ提案します。
- コンパイラ向けにフォーマットを最適化するのが簡単です (ただし、2 番目のメソッドは高速な 'Concat' メソッドを使用するようにリファクタリングされる可能性があります)。
- 通常、書式設定は読みやすくなります (特に「派手な」書式設定の場合)。
- 書式設定とは、すべての変数に対して '.ToString' を暗黙的に呼び出すことを意味し、読みやすさに優れています。
- 「Effective C#」によると、.NET の「WriteLine」と「Format」の実装はめちゃくちゃで、すべての値の型をオートボックス化します (これは悪いことです)。「効果的な C#」では、「.ToString」呼び出しを明示的に実行することを推奨していますが、これは私見では偽物です (「効果的な C#」を参照)。 ジェフさんの投稿)
- 現時点では、書式設定タイプのヒントはコンパイラーによってチェックされないため、実行時エラーが発生します。ただし、これは将来のバージョンで修正される可能性があります。
String.Format を使用しますが、他の言語にローカライズできるように、リソース ファイル内にフォーマット文字列も含めます。単純な文字列連結を使用すると、これを行うことはできません。もちろん、その文字列をローカライズする必要がない場合は、これを考慮する必要はありません。それは文字列の目的によって異なります。
ユーザーに表示する場合は、必要に応じてローカライズできるように String.Format を使用します。 FXコップ 念のためスペルチェックしてみます:)
数字やその他の文字列以外のものが含まれている場合 (例:日付)、私は String.Format を使用します。 書式設定の制御.
SQLのようなクエリを構築するためのものであれば、私はこれを使用します リンク.
ループ内で文字列を連結する場合は、次を使用します 文字列ビルダー パフォーマンスの問題を回避するため。
ユーザーに表示されず、パフォーマンスに影響を与えない出力の場合は、私は String.Format を使用します。とにかく使用する習慣があり、慣れているだけです:)
読みやすくする必要があるもの (ほとんどのコード) を扱っている場合は、次の場合を除き、演算子オーバーロード バージョンを使い続けると思います。
- コードは何百万回も実行する必要がある
- 大量の連結を行っています (4 つ以上は 1 トンです)
- コードは Compact Framework を対象としています。
これらの状況のうち少なくとも 2 つでは、代わりに StringBuilder を使用します。
読みやすさを重視して選んでいます。変数の周囲にテキストがある場合は、フォーマット オプションを使用することをお勧めします。この例では:
Console.WriteLine("User {0} accessed {1} on {2}.",
user.Name, fileName, timestamp);
変数名がなくても意味はわかりますが、連結部分は引用符と + 記号が乱雑で目が混乱します。
Console.WriteLine("User " + user.Name + " accessed " + fileName +
" on " + timestamp + ".");
(マイクの例が気に入ったので拝借しました)
フォーマット文字列が変数名なしではあまり意味がない場合は、 concat を使用する必要があります。
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
format オプションを使用すると、変数名を読み取り、対応する数値にマッピングします。concat オプションではその必要はありません。私はまだ引用符と + 記号に混乱していますが、代わりに使用するものはもっとひどいものです。ルビー?
Console.WriteLine(p.FirstName + " " + p.LastName);
パフォーマンスの面では、format には文字列が必要であるため、format オプションは concat よりも遅くなることが予想されます。 解析された. 。この種の命令を最適化する必要があった覚えはありませんが、もし最適化した場合は、次のことを検討します。 string
のようなメソッド Concat()
そして Join()
.
フォーマットのもう 1 つの利点は、フォーマット文字列を構成ファイルに含めることができることです。エラー メッセージや UI テキストが非常に便利です。
結果をローカライズする場合は、String.Format が不可欠です。異なる自然言語では、データが同じ順序で存在しない可能性があるためです。
これは出力の複雑さに大きく依存すると思います。私はその時点で最も効果的なシナリオを選択する傾向があります。
ジョブに基づいて適切なツールを選択してください:D 見た目が最もきれいなものを選択してください!
私は後者の方も好みますが、現時点ではその立場を支持する合理的な議論はありません。
いいもの!
追加したばかりです
s.Start();
for (var i = 0; i < n; i++)
result = string.Concat(p.FirstName, " ", p.LastName);
s.Stop();
ceElapsedMilliseconds = s.ElapsedMilliseconds;
ceElapsedTicks = s.ElapsedTicks;
s.Reset();
そして、さらに高速です (両方の例で string.Concat が呼び出されていると思いますが、最初の例では何らかの変換が必要です)。
1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 249ms - 3571621 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 65ms - 944948 ticks
1000000 x result = string.Concat(p.FirstName, " ", p.LastName); took: 54ms - 780524 ticks
ここでの回答がすべてを網羅しているわけではないと思いますので、ここで少し補足させていただきます。
Console.WriteLine(string format, params object[] pars)
電話をかける string.Format
. 。「+」は文字列の連結を意味します。これは必ずしもスタイルと関係があるわけではないと思います。私は、状況に応じて 2 つのスタイルを混合する傾向があります。
短い答え
あなたが直面している決定は、文字列の割り当てに関係しています。簡単にしてみます。
持っていると言ってください
string s = a + "foo" + b;
これを実行すると次のように評価されます。
string tmp1 = a;
string tmp2 = "foo"
string tmp3 = concat(tmp1, tmp2);
string tmp4 = b;
string s = concat(tmp3, tmp4);
tmp
これは実際にはローカル変数ではありませんが、JIT 用の一時変数です (IL スタックにプッシュされます)。文字列をスタックにプッシュすると (例: ldstr
IL ではリテラルの場合)、文字列ポインターへの参照をスタックに置きます。
電話をかけた瞬間 concat
両方の文字列を含む利用可能な文字列参照がないため、この参照が問題になります。これは、.NET が新しいメモリ ブロックを割り当てて、そこに 2 つの文字列を埋める必要があることを意味します。これが問題となる理由は、割り当てに比較的コストがかかるためです。
これにより、質問は次のように変わります。どうすれば数を減らすことができますか concat
オペレーション?
したがって、大まかな答えは次のとおりです。 string.Format
>1 連結の場合、「+」は 1 連結で問題なく機能します。細かいパフォーマンスの最適化を気にしないのであれば、 string.Format
一般的なケースでは問題なく動作します。
文化に関するメモ
そして、文化と呼ばれるものがあります...
string.Format
を使用できるようにします CultureInfo
あなたの書式設定で。単純な演算子「+」は現在のカルチャを使用します。
これは、ファイル形式や f.ex を作成している場合に特に重要な注意事項です。 double
文字列に「追加」する値。異なるマシンでは、使用しないと異なる文字列になる可能性があります。 string.Format
明示的に CultureInfo
.
フェデックス'を変更した場合に何が起こるかを考えてください。 「」のために、コンマセーパーバリューファイルを書きながら...オランダ語では小数点区切り文字はカンマなので、ユーザーは「面白い」驚きを感じるかもしれません。
より詳しい回答
文字列の正確なサイズが事前にわからない場合は、このようなポリシーを使用して、使用するバッファーを過剰に割り当てることをお勧めします。まずスラックスペースが埋められ、その後データがコピーされます。
拡張とは、メモリの新しいブロックを割り当て、古いデータを新しいバッファにコピーすることを意味します。その後、メモリの古いブロックを解放できます。この時点で結論は次のようになります。成長には費用がかかる作業です。
これを行う最も現実的な方法は、超過割り当てポリシーを使用することです。最も一般的なポリシーは、バッファを 2 のべき乗で過剰に割り当てることです。もちろん、それよりももう少し賢く行う必要があります (128 文字が必要であることがすでにわかっている場合、1、2、4、8 と増やすのは意味がありません) が、全体像はわかります。このポリシーにより、上で説明したコストのかかる操作があまり必要なくなるようになります。
StringBuilder
は、基本的に基になるバッファーを 2 の累乗で過剰に割り当てるクラスです。 string.Format
用途 StringBuilder
フードの下。
これにより、決定は、overlocate-and-append(-multiple) (w/w.o.) の間の基本的なトレードオフになります。カルチャ)、または単に割り当てて追加するだけです。
個人的には、2 番目のものは、使用しているものすべてが出力される直接の順序になっているためです。一方、最初の場合は、{0} と {1} を適切な変数と一致させる必要があるため、間違いが発生しやすくなります。
少なくとも、変数の型を間違えると全体が爆発してしまう C++ sprintf ほど悪くはありません。
また、2 番目はすべてインラインであり、すべての {0} について検索や置換を行う必要がないため、後者の方が高速になるはずです...確かなことは分かりませんが。
実際、私は最初の方が気に入っています。テキストに多くの変数が混在していると、読みやすく見えるからです。さらに、string.Format()、ええと、format を使用すると、引用符を扱うのが簡単になります。ここは まともな分析 文字列連結の。
私は常に string.Format() ルートを選択してきました。ネイサンの例のようにフォーマットを変数に保存できることは大きな利点です。場合によっては、変数を追加することもありますが、複数の変数が連結されると、書式設定を使用するようにリファクタリングします。
ああ、完全を期すために、以下は通常の連結よりも数ティック高速です。
Console.WriteLine(String.Concat(p.FirstName," ",p.LastName));
最初のもの (形式) の方が私には良いように思えます。読みやすくなり、余分な一時文字列オブジェクトを作成する必要がなくなります。
StringBuilder がこれらのテストでどのような立場にあるのか興味がありました。結果は以下の通り…
class Program {
static void Main(string[] args) {
var p = new { FirstName = "Bill", LastName = "Gates" };
var tests = new[] {
new { Name = "Concat", Action = new Action(delegate() { string x = p.FirstName + " " + p.LastName; }) },
new { Name = "Format", Action = new Action(delegate() { string x = string.Format("{0} {1}", p.FirstName, p.LastName); }) },
new { Name = "StringBuilder", Action = new Action(delegate() {
StringBuilder sb = new StringBuilder();
sb.Append(p.FirstName);
sb.Append(" ");
sb.Append(p.LastName);
string x = sb.ToString();
}) }
};
var Watch = new Stopwatch();
foreach (var t in tests) {
for (int i = 0; i < 5; i++) {
Watch.Reset();
long Elapsed = ElapsedTicks(t.Action, Watch, 10000);
Console.WriteLine(string.Format("{0}: {1} ticks", t.Name, Elapsed.ToString()));
}
}
}
public static long ElapsedTicks(Action ActionDelg, Stopwatch Watch, int Iterations) {
Watch.Start();
for (int i = 0; i < Iterations; i++) {
ActionDelg();
}
Watch.Stop();
return Watch.ElapsedTicks / Iterations;
}
}
結果:
Concat: 406 ticks Concat: 356 ticks Concat: 411 ticks Concat: 299 ticks Concat: 266 ticks Format: 5269 ticks Format: 954 ticks Format: 1004 ticks Format: 984 ticks Format: 974 ticks StringBuilder: 629 ticks StringBuilder: 484 ticks StringBuilder: 482 ticks StringBuilder: 508 ticks StringBuilder: 504 ticks
MCSD の準備資料によると、Microsoft は非常に少数の連結 (おそらく 2 ~ 4) を処理する場合に + 演算子を使用することを推奨しています。理由はまだわかりませんが、考慮すべき点です。