> 作業効率UP!! オススメのモバイルモニターを紹介

【C#】ハイパフォーマンスな文字列連結方法について

当ページのリンクには広告が含まれています。
  • URLをコピーしました!

C# では、文字列を連結する方法は複数あります。

文字列連結方法

  • 文字列連結演算子+を使用する
  • メソッドstring.Formatを使用する
  • メソッドstring.Contactを使用する
  • 文字列補間式$ を使用する
  • StringBuilderクラスのメソッドAppendを使用する

文字列連結は一般的に使用されますが、大量の文字列を結合する場合、処理のパフォーマンスが問題になることがあります。特に、文字列は不変オブジェクトであるため、頻繁な操作はメモリ使用量や処理速度に影響を及ぼします。

本記事では効率的な文字列の連結方法を確認するために、一定回数 文字列を連結した時の処理時間や使用中のメモリ領域を比較しています。処理時間の測定や使用中のメモリ領域の測定には BenchmarkDotNet を使用しています。

オススメの参考書

C#の使い方が丁寧に解説しており、「基礎からしっかりと学びたい」という初心者の方にオススメの一冊です。サンプルコードも記載してあり、各章の最後に復習問題があるので理解度を確認しながら読み進めることができます。新しい C# のバージョンにも対応している書籍です。

記事の内容

おさらい

冒頭で述べたように C# では文字列を連結する方法はいくつか存在します。

文字列連結方法

  • 文字列連結演算子+を使用する
  • メソッドstring.Formatを使用する
  • メソッドstring.Concatを使用する
  • 文字列補間式$ を使用する
  • StringBuilderクラスのメソッドAppendを使用する

それぞれの使い方は次の通りです。

文字列連結演算子+を使用する場合

string name = "山内";
int age = 40;
string message = name + "さんは、今年" + age + "歳になりました。";
Console.WriteLine(message);

string.Formatメソッドを使用する場合

string name = "山内";
int age = 40;
string message = string.Format("{0}さんは、今年{1}歳になりました。", name, age);
Console.WriteLine(message);

string.Concatメソッドを使用する場合

string name = "山内";
int age = 40;
string message = string.Concat("{0}さんは、今年{1}歳になりました。", name, age);
Console.WriteLine(message);

文字列補間式$を使用する場合

string name = "山内";
int age = 40;
string message = $"{name}さんは、今年{age}歳になりました。";
Console.WriteLine(message);

StringBuilderクラスのメソッドAppendを使用する場合

string name = "山内";
int age = 40;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append(name).Append("さんは、今年").Append(age).Append("歳になりました。");
Console.WriteLine(stringBuilder.ToString());

ベンチマーク

文字列の連結処理の時間を測定するために、BenchmarkDotNetというライブラリを使用します。事前に NuGet からインストールしておきます。

https://benchmarkdotnet.org/

今回は各方法毎に1000回ループする中で、文字列とループ回数の変数を連結します。連結前にループ回数の変数はint型から文字列型へ変換しておきます。

以下のコードを実行して処理時間を比較します。

using System.Text;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace ConsoleApp
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<Test>();
        }
    }

    [MemoryDiagnoser]
    [RankColumn]
    public class Test
    {
        private const int MaxLoop = 1000;
        private const string Prefix = "[";
        private const string Suffix = "]";

        [Benchmark]
        public string StringConcatenationOperator()
        {
            var result = string.Empty;

            for (var i = 0; i < MaxLoop; i++)
            {
                result = result + Prefix + i.ToString() + Suffix;
            }

            return result;
        }

        [Benchmark]
        public string StringFormat()
        {
            var result = string.Empty;

            for (var i = 0; i < MaxLoop; i++)
            {
                result = string.Format("{0}{1}{2}{3}", result, Prefix, i.ToString(), Suffix);
            }

            return result;
        }

        [Benchmark]
        public string StringContact()
        {
            var result = string.Empty;

            for (var i = 0; i < MaxLoop; i++)
            {
                result = string.Concat("{0}{1}{2}{3}", result, Prefix, i.ToString(), Suffix);
            }

            return result;
        }

        [Benchmark]
        public string StringInterpolation()
        {
            var result = string.Empty;

            for (var i = 0; i < MaxLoop; i++) 
            {
                result = $"{result}{Prefix}{i.ToString()}{Suffix}"; 
            }

            return result;
        }


        [Benchmark]
        public string StringBuilderAppend()
        {
            var stringBuilder = new StringBuilder();

            for (var i = 0; i < MaxLoop; i++)
            {
                stringBuilder.Append(Prefix).Append(i.ToString()).Append(Suffix);
            }

            return stringBuilder.ToString();
        }
    }
}

結果

コードを実行しベンチマークした結果が以下になります。

|                      Method |        Mean |     Error |    StdDev | Rank |      Gen 0 |    Gen 1 | Gen 2 |   Allocated |
|---------------------------- |------------:|----------:|----------:|-----:|-----------:|---------:|------:|------------:|
| StringConcatenationOperator |   492.10 us |  9.657 us |  9.033 us |    2 |  7916.0156 |        - |     - |  4753.23 KB |
|                StringFormat | 1,529.51 us | 23.974 us | 21.252 us |    4 | 23595.7031 |  99.6094 |     - | 14225.25 KB |
|               StringContact | 1,458.17 us | 22.857 us | 19.086 us |    3 | 27000.0000 | 142.5781 |     - | 16657.55 KB |
|         StringInterpolation |   491.22 us |  9.563 us |  9.821 us |    2 |  7916.5039 |        - |     - |  4753.23 KB |
|         StringBuilderAppend |    67.49 us |  1.383 us |  1.294 us |    1 |    75.5615 |        - |     - |    45.53 KB |

この結果からStringBuilder.Appendメソッドが処理時間が速い且つメモリの消費領域も少なく、パフォーマンスに優れていることが判りました。

文字列連結演算子+と文字列補間$のパフォーマンスはほぼ同じ結果でしたが、実際にコードで使用することを考えると直感的に読みやすい文字列補間$を優先して使用した方がいいと思います。

string.Concatメソッドやstring.Formatメソッドはパフォーマンスを重視するなら使用しないことを推奨します。

まとめ

この記事では効率的な文字列の連結方法をBenchmarkDotNetを使用してベンチマークしました。

ベンチマークした結果、パフォーマンスが最も優れていたのはStringBuilder.Appendメソッドでした。高パフォーマンスを必要とする部分においては、StringBuilder.Appendを使用することで処理時間とメモリの使用領域を向上させる可能性があります。

コードの可読性やメンテナンス性を重視する場合では、文字列補間式$が適しています。

この機会にぜひ自身のコードを振り返ってみてはいかがでしょうか。

以上、最後まで読んで頂きありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
記事の内容