C#で動的な文字列を作成する際、古くから使われているstring.Formatと、C# 6.0で登場した文字列補間$""のどちらを使うべきか迷うことはありませんか?
本記事では、これら2つの手法の根本的な違いから、最新の.NETにおける驚きのパフォーマンス差、そして現場で役立つ具体的な使い分けガイドまでを詳しく解説します。
string.Formatの基本と特徴
string.Formatは、C#の初期から存在する文字列フォーマット手法です。
書式指定文字列の中にインデックス番号を記述し、後続の引数でその値を置換する仕組みを持っています。
string.Formatの構文と基本的な使い方
このメソッドは、第一引数にテンプレートとなる文字列を、第二引数以降に埋め込む値を渡します。
using System;
public class Program
{
public static void Main()
{
string name = "アリス";
int age = 25;
// {0} や {1} がプレースホルダーとして機能します
string message = string.Format("名前: {0}, 年齢: {1}歳", name, age);
Console.WriteLine(message);
}
}
名前: アリス, 年齢: 25歳
複合書式指定の柔軟性
string.Formatの強みは、書式を外部から注入しやすい点にあります。
例えば、多言語対応(ローカライズ)を行う際に、リソースファイルからフォーマット文字列のみを取得し、実行時に値を流し込むといった処理に適しています。

上記のように、string.Formatは「テンプレート」と「データ」を明確に分離して管理できるのが特徴です。
しかし、引数の数が増えるとインデックスの対応関係が分かりにくくなるという欠点もあります。
文字列補間の基本と特徴
C# 6.0から導入された文字列補間$""は、文字列の中に直接変数を記述できる直感的な記法です。
現在、C#開発において最も標準的な方法となっています。
文字列補間の構文と基本的な使い方
文字列の先頭に$記号を付けることで、波括弧{}の中に変数名や式を直接記述できるようになります。
using System;
public class Program
{
public static void Main()
{
string product = "ノートPC";
double price = 128000.5;
// 変数名を直接波括弧の中に記述できる
string message = $"製品名: {product}, 価格: {price:N0}円";
Console.WriteLine(message);
}
}
製品名: ノートPC, 価格: 128,001円
直感的なコード記述
文字列補間を使用すると、コードを読んだ瞬間にどのような文字列が出来上がるかを一目で理解できます。
string.Formatのように、文字列の末尾にある引数リストとインデックスを交互に確認する必要がありません。

string.Formatと文字列補間の決定的な違い
これら2つの手法には、見た目以上の大きな違いがいくつか存在します。
1. コンパイル時のチェック
string.Formatの場合、インデックスの指定ミス(例: 引数が2つしかないのに{2}と書く)は、実行時に例外(FormatException)が発生します。
これに対し、文字列補間はコンパイル時に変数名の正しさがチェックされるため、ミスを事前に防ぐことができます。
2. パフォーマンスとメモリ効率
かつては「文字列補間は内部的にstring.Formatを呼び出しているだけ」と言われていました。
しかし、最新の.NET(.NET 6以降)では、InterpolatedStringHandlerという仕組みが導入され、文字列補間の方が圧倒的に高速で、メモリ割り当て(アロケーション)も少なくなっています。
| 項目 | string.Format | 文字列補間 ($””) |
|---|---|---|
| 可読性 | 低い (インデックス管理が必要) | 非常に高い |
| 安全性 | 実行時エラーの可能性あり | コンパイル時にエラーを検知 |
| 速度 | 標準的 | 非常に高速 (最新.NETの場合) |
| メモリ消費 | ボクシングが発生しやすい | 最適化により極めて低い |
速度比較:ベンチマークによる検証
実際に、大量のループ内で文字列を生成した場合のパフォーマンス差を見てみましょう。
ここでは、数値を文字列に埋め込む際の処理速度を比較します。
using System;
using System.Text;
// 概念的なベンチマークコード
public class SpeedTest
{
public void RunFormat(int iterations)
{
string result;
for (int i = 0; i < iterations; i++)
{
// objectへのボクシングが発生する
result = string.Format("Count: {0}", i);
}
}
public void RunInterpolation(int iterations)
{
string result;
for (int i = 0; i < iterations; i++)
{
// 最新.NETではボクシングを回避する最適化が走る
result = $"Count: {i}";
}
}
}

パフォーマンスの裏側にある「ボクシング」
string.Formatは、引数をobject型として受け取ります。
そのため、intやDateTimeのような値型を渡すと、ボクシングと呼ばれるヒープメモリへの割り当てが発生し、これが負荷となります。
一方、最新の文字列補間は、コンパイラが型を認識したまま効率的に処理するため、不要なメモリ消費を抑えることができます。
高度な書式設定と応用テクニック
どちらの手法でも、数値や日付の書式を細かく指定することが可能です。
桁数指定とアライメント
右寄せや左寄せ、小数点以下の桁数指定なども簡単に行えます。
using System;
public class Program
{
public static void Main()
{
double pi = 3.14159;
int id = 42;
// 小数点第2位まで表示
Console.WriteLine($"円周率: {pi:F2}");
// 10文字分の幅を確保して右寄せ(正の数)・左寄せ(負の数)
Console.WriteLine($"ID: [{id,10}]");
Console.WriteLine($"ID: [{id,-10}]");
}
}
円周率: 3.14
ID: [ 42]
ID: [42 ]
Raw文字列リテラルとの組み合わせ (C# 11〜)
C# 11からは、Raw文字列リテラル""" """が登場しました。
これにより、JSONやXMLなどの複数行にわたる文字列を、エスケープシーケンスなしで美しく記述できるようになりました。
string name = "田中";
// $$$ を使うことで、JSON内の {} と干渉せずに変数を埋め込める
string json = $$$"""
{
"user": {
"name": "{{{name}}}",
"type": "admin"
}
}
""";

結局どちらを使うべきか?使い分けのガイドライン
現代のC#開発においては、原則として「文字列補間」を使用するのがベストプラクティスです。
文字列補間を使うべきケース
- 通常のログ出力や画面表示
- 変数を1〜5個程度埋め込む場合
- パフォーマンスが求められるループ処理内
- コードの可読性を最優先したい場合
string.Formatを使うべきケース
- フォーマット文字列自体を外部(DBや設定ファイル)から取得する場合
- 古いバージョンの.NET Framework環境で、動的なフォーマットが必要な場合
string.Formatが完全に廃止されることはありませんが、それは「テンプレートの外部注入」という特殊な用途があるためです。
プログラム内で完結する通常の文字列操作であれば、文字列補間を選ばない理由はありません。
まとめ
C#におけるstring.Formatと文字列補間は、どちらも同様の結果を得られますが、その内部動作と効率には大きな差があります。
文字列補間は、単なる書きやすさの向上だけでなく、最新の.NETにおいてはメモリ効率と実行速度の面でも優位に立っています。
コンパイル時の型チェックによる安全性も相まって、現在の開発では第一選択肢となります。
一方で、多言語対応などで書式自体をデータとして扱う場合には、string.Formatが依然として強力なツールとなります。
それぞれの特性を理解し、基本は文字列補間、特殊な要件にはstring.Formatというルールで使い分けていきましょう。
