1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめてのひとりアドカレ2024 by hayamiAdvent Calendar 2024

Day 16

【C#】文字列結合時とStringBuilder使用時のパフォーマンス差異

Last updated at Posted at 2024-12-23

O'Reilly『C#クックブック』 p.40「レシピ2.1 文字列の効率的な処理」について、知らなかったのでまとめた。

C#の文字列を結合する時にどのようなメソッドを実装したほうが効率的か、という項目。


p.40-41 より、コード引用 + 筆者コメントによる補足付与

処理対象のInvoiceItemクラス

  • 扱うデータのクラスを作成
InvoiceItemクラス
public class InvoiceItem{
    //費用
    public decimal Cost { get; set; }
    //費用に関する説明
    public string Description { get; set; }
}

サンプルデータを作成

  • サンプルとしてデータがあればいいので、ランダムな値を生成し、それを費用としている
GetInvoiceItemsメソッド
static List<InvoiceItem> GetInvoiceItem(){
    //InvoiceItem型のインスタンスを作成
    var Items = new List<InvoiceItem>();
    //ランダム抽出するため、ライブラリのランダムクラスのインスタンスを作成
    var rand = new Random();
    for (int i=0; i<100; i++)
        items.Add(
            new InvoiceItem{
                cost = rand.Next(i), //ランダム値を生成
                Discription = "請求事項#" + (i+1)
        });
    return items;
}

1. 文字列処理の非効率なメソッド

  • 単純な文字列結合のほうが、直感的でコードにもしやすく、多くの場合で採用される
  • しかし同時にアプリケーションのパフォーマンスを落としてしまう場合もある
    • なぜか?
      • 文字列結合のたびに高価なメモリ確保処理が実行されるため
    • そもそも
      • C#のstring型は不変であり、一度作成された文字列はその内容を変更することができない
    • つまり
      • +=演算子を使用して結合する文字列を取り出すたびに、新しいstring型のために新しいメモリ領域が作られ、一度結合できればいいものが永遠にメモリ上に残り続ける
      • これは、結合したい値が増えるほどメモリを逼迫していく
      • C#はガベージコレクション(GC)が自動で行われるが、ガベージコレクションによる不要オブジェクトの回収にも負荷がかかるため、回収負荷の点でも増加にはたらきネガティブ
DoStringConcatenationメソッド
static string DoStringConcatenation(List<InvoiceItem> lineItems){
    //文字列をくっつけるための空の文字列を作成
    string report = "";
    foreach(var item in lineItems){
        //「$」および「:C」はC#の文字列間補完
        //作成したサンプルデータから一つずつ取り出してガッチャンコ
        report += $"{item.Cost:C}-{item.Discription}\n";
    }
    //文字列結合した文字列を返す
    return report;
}

※文字列間補完について

文字列間補完式を使うと、引数として使用したい値をそのまま埋め込むことができる


2. より効率的な文字列処理のメソッド

  • StringBuilderは、可変長の文字列バッファを内部に持つクラス
  • 初期化時にバッファの容量を確保し、その中で文字列を変更・結合する
  • 再割り当てが必要になった場合でもはじめに確保したバッファを使いまわすことで、余分なメモリ割り当てとコピーを避けることができる
DoStringBuilderConcatenationメソッド
static string DoStringBuilderConcatenation(List<InvoiceItem> lineItems){
    //まずreportBuilderというインスタンスを生成
    var reportBuilder = new StringBuilder();

    foreach(var item in lineItems){
        //生成したインスタンスreportBuilderでAppendTextを繰り返し呼び出している
        //記法、こちらも詳しくは「文字列間補完」を参照
        reportBuilder.Append($"{item.Cost:C}-{item.Discription}\n");
    }
    return reportBuilder.ToString();
}

※StringBuilderについて

文字列結合を使うか、StringBuilderを使用するかの分岐点

The answer : 4回以上文字列を結合させるか否か

p.42

経験則として、4回以上文字列を結合させる場合にはStringBuilderを使用した方がパフォーマンス的に有利です。

余談

業務でもたまに文字列結合の処理を見かけることがありますが、観測範囲ではStringBuilderを使用しているシーンは… なかったかも…

細かいTipsですが、ちりつもでパフォーマンスの悪化に繋がるため、心掛けていきたいです。


1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?