+演算子による文字列連結の問題点
C#で文字列を連結する最も簡単な方法は + や += 演算子を使うことです。
string message = "User: ";
message += "Admin"; // "User: Admin"
この方法は、数回の連結であれば全く問題ありません。しかし、forやforeachといったループ処理の中で、何百回、何千回と文字列を += で連結し続けると、深刻なパフォーマンス低下を引き起こす可能性があります。
C#のstringは「不変(Immutable)」
パフォーマンスが低下する原因は、C#のstring型が「不変(Immutable)」であるという特性にあります。
string型の変数は、一度作成されると、その中身(メモリ上のデータ)を変更することはできません。message += "Admin"; という操作は、message変数の末尾にデータを追加しているように見えますが、内部的には以下の処理が行われています。
"User: "と"Admin"の両方を格納できる、新しいメモリ領域を確保します。- そこに
"User: Admin"という新しい文字列を作成します。 message変数が、この新しい文字列を参照するように切り替えます。- 古い
"User: "の文字列は、やがてガベージコレクション(GC)によって破棄されます。
ループ内でこの操作を10,000回繰り返すと、10,000個の不要な中間文字列オブジェクトが生成・破棄されることになり、これがCPUとメモリに大きな負荷を与えます。
StringBuilderによる解決
この問題を解決するために、C#にはSystem.Text名前空間にStringBuilderクラスが用意されています。
StringBuilderは、内部に可変(変更可能)な文字バッファ(編集領域)を持っています。stringとは異なり、StringBuilderに対する文字列の追加(Append)は、既存のバッFAにデータを書き足していくだけで、新しいオブジェクトを(バッファが満杯になるまでは)生成しません。
これにより、ループ内で大量の文字列を連結する場合でも、非常に高速に処理を行うことができます。
StringBuilder の基本的な使い方
StringBuilderを使用するには、まずSystem.Text名前空間をusingし、クラスをインスタンス化します。
主なメソッド:
Append(string value): バッファの末尾に文字列を追加します。AppendLine(string value): バッファの末尾に文字列を追加し、さらに改行文字を追加します。ToString(): 最後に、バッファ内に構築されたすべての文字列を、単一のstring型として取り出します。
コード例:foreachループでの使用
List<string>(文字列のリスト)の各要素を、AppendLineを使用して1行ずつ連結し、最終的なレポート文字列を生成する例です。
using System;
using System.Text; // StringBuilder を使用するために必要
using System.Collections.Generic;
public class StringBuilderExample
{
public static void Main()
{
// 処理対象のデータリスト
var reportLines = new List<string>
{
"Line 1: Server OK",
"Line 2: Database Connected",
"Line 3: User 'Guest' login",
"Line 4: Process Complete"
};
// 1. StringBuilder のインスタンスを生成
var logBuilder = new StringBuilder();
// 2. ループの「外」でインスタンスを生成するのが重要
logBuilder.AppendLine("--- Log Report Start ---");
// 3. ループ内で Append / AppendLine を呼び出す
foreach (var line in reportLines)
{
// AppendLine は、文字列を追加した後に改行を追加する
logBuilder.AppendLine(line);
}
logBuilder.AppendLine("--- Log Report End ---");
// 4. 最後に ToString() で結果を一つの string として取り出す
string finalLog = logBuilder.ToString();
// 5. 構築された文字列を出力
Console.WriteLine(finalLog);
}
}
出力結果:
--- Log Report Start ---
Line 1: Server OK
Line 2: Database Connected
Line 3: User 'Guest' login
Line 4: Process Complete
--- Log Report End ---
まとめ
stringの+や+=による連結は、コードがシンプルで読みやすいですが、ループ内で大量に実行するとパフォーマンスが低下します。
- 少数の連結:
string +や文字列補間($)で問題ありません。 forやforeachループ内の大量の連結: 必ずStringBuilderを使用します。
StringBuilderは、不要なオブジェクト生成とガベージコレクションの負荷を劇的に削減し、アプリケーションの応答性を維持するための必須のツールです。
