コンソール出力やログ、UIテキストに変数値を埋め込みたいとき、C#の文字列補間($)は最も簡単で読みやすい手段です。
複雑な連結やstring.Format
を避け、統一的に書式指定やカルチャ制御も行えます。
本記事では基本から実用例、パフォーマンス、落とし穴までを整理して解説します。
C#の<文字列補間($)の基本:変数を簡単に埋め込む
基本の書き方:$”Hello {name}” と補間式のルール
文字列補間は、先頭に$
を付けた文字列リテラルの中に{...}
でC#の式を直接書ける機能です。
最小の例は次のとおりです。
using System;
class Program
{
static void Main()
{
var name = "Taro";
var age = 28;
Console.WriteLine($"Hello {name}, you are {age} years old.");
}
}
Hello Taro, you are 28 years old.
補間式({...}
)には、変数だけでなくプロパティ参照、メソッド呼び出し、演算式など一般的なC#の式を書けます。
補間式の基本構文は次のいずれかです。
{expr}
{expr,alignment}
{expr:format}
{expr,alignment:format}
ここでalignment
は幅と寄せ方向(正=右寄せ、負=左寄せ)、format
は標準またはカスタム書式指定文字列です。
文字列結合やstring.Formatとの違い・メリット(可読性・型安全)
従来の+
による結合やstring.Format
と比較すると、補間は次の点で有利です。
- 可読性が高く、出力レイアウトをコードの見た目から直感的に把握しやすいです。
- 引数の個数や順序の不一致といった
string.Format
特有のミスを避けやすいです。補間では各式がその場で型チェックされ、不要なボックス化も最適化されます。 - 書式指定やカルチャの適用を簡潔に書けます(後述の
FormattableString
も活用可能)。
簡単な比較表を示します。
手法 | 記述量 | 可読性 | 型安全(引数ミスの回避) | 書式指定 |
---|---|---|---|---|
+ 結合 | 多くなりがち | 低い | 低い | 困難 |
string.Format | 中程度 | 中 | 中(添字ミスが起こりうる) | 可 |
文字列補間 $ | 少ない | 高い | 高い | 容易 |
エスケープの基礎:波かっこや引用符の扱い
補間で{
や}
を文字として出したい場合は、{{
と}}
と二重に書きます。
通常の文字列でダブルクォート("
)を含めるには\"
とエスケープします。
逐語的文字列(@
)や原文(raw)文字列を使うとエスケープが簡略化できます(後述)。
using System;
class Program
{
static void Main()
{
var section = "Settings";
Console.WriteLine($"JSON starts with {{ and ends with }} in section \"{section}\".");
}
}
JSON starts with { and ends with } in section "Settings".
書式指定とフォーマット:数値・日時・桁揃え
数値フォーマットの書き方:{value:0.##}/{value:N0}
数値の小数点や桁区切りをコントロールするには、:
に続けて書式を指定します。
using System;
class Program
{
static void Main()
{
double x = 1234.5;
Console.WriteLine($"固定小数点 0.##: {x:0.##}");
Console.WriteLine($"桁区切り N0 : {x:N0}");
Console.WriteLine($"百分率 P1 : {0.1234:P1}");
Console.WriteLine($"16進 X : {255:X}");
}
}
出力例(ja-JPカルチャの一例):
固定小数点 0.##: 1234.5
桁区切り N0 : 1,235
百分率 P1 : 12.3 %
16進 X : FF
日付時刻フォーマット:{date:yyyy/MM/dd HH:mm}
日時も同様に書式化できます。
using System;
class Program
{
static void Main()
{
var dt = new DateTime(2025, 8, 17, 14, 5, 9);
Console.WriteLine($"ISO風 : {dt:yyyy-MM-ddTHH:mm:ss}");
Console.WriteLine($"日本式 : {dt:yyyy/MM/dd HH:mm}");
Console.WriteLine($"曜日込み : {dt:yyyy年M月d日 (ddd)}");
}
}
ISO風 : 2025-08-17T14:05:09
日本式 : 2025/08/17 14:05
曜日込み : 2025年8月17日 (日)
アライメント(幅・左寄せ/右寄せ):{value,10} と {text,-20}
列揃えはレポート出力で便利です。
正の幅は右寄せ、負は左寄せになります。
書式指定と併用できます。
using System;
class Program
{
static void Main()
{
var items = new (string Name, int Qty, decimal Price)[]
{
("Pen", 12, 120.5m), ("Notebook", 3, 450m), ("Eraser", 20, 80m)
};
Console.WriteLine($"{"Item",-10} {"Qty",5} {"Price",10}");
foreach (var (name, qty, price) in items)
{
Console.WriteLine($"{name,-10} {qty,5} {price,10:N0}");
}
}
}
Item Qty Price
Pen 12 121
Notebook 3 450
Eraser 20 80
CultureInfoとFormattableString.Invariantでカルチャを制御
文字列補間は基本的に現在のカルチャに従って書式化されます。
カルチャを明示的に固定したいときはFormattableString
を活用します。
using System;
using System.Globalization;
class Program
{
static void Main()
{
double pi = Math.PI;
// 現在カルチャ(例: fr-FRを一時適用)
var fr = new CultureInfo("fr-FR");
CultureInfo.CurrentCulture = fr;
string current = $"PI = {pi:N2}";
string invariant = FormattableString.Invariant($"PI = {pi:N2}");
string ja = FormattableStringFactory.Create($"PI = {pi:N2}")
.ToString(new CultureInfo("ja-JP"));
Console.WriteLine($"Current(fr-FR): {current}");
Console.WriteLine($"Invariant : {invariant}");
Console.WriteLine($"ja-JP : {ja}");
}
}
Current(fr-FR): PI = 3,14
Invariant : PI = 3.14
ja-JP : PI = 3.14
Invariantにより小数点や桁区切りが安定し、ログやシリアライズ時の差異を避けられます。
文字列リテラルとエスケープ:@$ と raw 文字列の使い分け
逐語的文字列 @$”…” で改行・バックスラッシュをそのまま書く
逐語的文字列(@
)はバックスラッシュや改行をエスケープなしで記述できます。
補間と併用する場合、$@"..."
と@$"..."
は同じ意味です。
using System;
class Program
{
static void Main()
{
var dir = @"C:\Program Files\App";
var file = "config.json";
Console.WriteLine($@"Path: {dir}\{file}
この行は改行をそのまま含みます。");
}
}
Path: C:\Program Files\App\config.json
この行は改行をそのまま含みます。
逐語的文字列内でダブルクォートを文字として出すには""
(二重のダブルクォート)を使います。
raw 文字列リテラル $”””…{expr}…””” の補間と複数行
C# 11のraw文字列リテラルは、"""
で囲みエスケープ不要、複数行を自然に書けます。
補間したい場合は先頭に$
を付けます。
using System;
class Program
{
static void Main()
{
var name = "Taro";
var json = $"""
{
"user": "{name}",
"message": "He said, ""Hello!"""
}
""";
Console.WriteLine(json);
}
}
{
"user": "Taro",
"message": "He said, "Hello!""
}
raw文字列は閉じ区切りのインデントを基準に先頭の共通インデントが取り除かれるため、コードのインデントを保ったまま整った文字列が得られます。
補間と中括弧の衝突を避けたいケース(JSONや正規表現など)では、$
を複数付けて「何個の波かっこで補間を開始するか」を調整できます。
$
の数に応じて、補間開始・終了には同数の{
と}
が必要になります。
using System;
class Program
{
static void Main()
{
var token = "XYZ";
// $ が1つ: {expr} が補間。生の { は {{ と二重に書く必要がある。
var s1 = $"""
JSON: {{ "token": "{token}" }}
""";
// $ が2つ: {{expr}} が補間。生の { は単体で書ける(JSONに最適)。
var s2 = $$"""
JSON: { "token": "{{token}}" } // ← {{token}} が補間される
""";
Console.WriteLine(s1);
Console.WriteLine(s2);
}
}
JSON: { "token": "XYZ" }
JSON: { "token": "XYZ" }
波かっこ/引用符を文字として出す際の注意点
- 通常/逐語的の補間では、リテラルの
{
と}
は{{
と}}
にエスケープします。 - raw補間で
$
を複数付けた場合、補間開始の{
の個数=$
の個数に一致させます。JSONのように{
が多い場合は$$"""
や$$$"""
で補間のしきい値を上げると読みやすくなります。 - 逐語的では
"
を""
と記述、通常文字列では\"
を使います。rawではそのまま"
を書けます(区切りの"""
と衝突する場合は区切りの"
の数を増やして対応可能です)。
補間式の書き方と実用例
変数・式の埋め込み:{x + y}/{obj.Property}/{Method()}
補間にはあらゆる式を入れられます。
using System;
class Program
{
static int Add(int a, int b) => a + b;
static void Main()
{
int x = 10, y = 32;
var obj = new { First = "Hanako", Last = "Yamada" };
Console.WriteLine($"Sum = {x + y}");
Console.WriteLine($"Name = {obj.Last}, {obj.First}");
Console.WriteLine($"Add(x,y) = {Add(x, y)}");
}
}
Sum = 42
Name = Yamada, Hanako
Add(x,y) = 42
条件演算子・null対策:{flag ? “ON” : “OFF”}/{name ?? “N/A”}
条件分岐やnull対策を直書きできます。
読みやすさのために必要に応じて括弧でくくると安心です。
using System;
class Program
{
static void Main()
{
bool flag = false;
string? name = null;
Console.WriteLine($"Status: {(flag ? "ON" : "OFF")}");
Console.WriteLine($"User : {name ?? "N/A"}");
}
}
Status: OFF
User : N/A
ロギング・例外メッセージ・UI表示での実例
ログや例外、UIテキスト生成など、実務での活用例です。
using System;
class Program
{
static void Main()
{
var userId = 12345;
var when = DateTime.UtcNow;
// ログ(シンプルな例)
Console.WriteLine($"[INFO] User {userId} logged in at {when:O} (UTC)");
// 例外メッセージ
try
{
throw new InvalidOperationException(
$"User {userId} cannot access this resource at {when:yyyy/MM/dd HH:mm:ss}.");
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] {ex.Message}");
}
// UIテキスト
var qty = 7;
var price = 1200m;
Console.WriteLine($"合計:{qty} 点(小計 {qty * price:N0} 円)");
}
}
[INFO] User 12345 logged in at 2025-08-17T05:05:09.0000000Z (UTC)
[ERROR] User 12345 cannot access this resource at 2025/08/17 14:05:09.
合計:7 点(小計 8,400 円)
パフォーマンス最適化とベストプラクティス
大量連結の比較:文字列補間 vs StringBuilder/AppendFormat
小規模な結合や1回限りの組み立てでは、補間はJITの最適化(string.Concat
相当)により高速かつ簡潔です。
一方、ループで大量に連結する場合はStringBuilder
に分があります。
using System;
using System.Text;
class Program
{
static void Main()
{
// 大量連結は StringBuilder が有利
var sb = new StringBuilder(capacity: 16_000);
for (int i = 0; i < 1000; i++)
{
sb.Append($"{i,5}: {i * i,8:N0}\n"); // 補間自体はOKだが、ビルダーに直接Appendする形が効率的
}
string table = sb.ToString();
Console.WriteLine(table.Split('\n')[0]); // 先頭行だけ確認
}
}
0: 0
補間をStringBuilder.AppendFormat
に置き換えるより、上記のように補間で1行ずつ作ってAppend
する、もしくは文字列補間ハンドラ対応のAppend
(.NET 8のAppendInterpolatedStringHandler
など)を活用すると効率と可読性を両立できます。
Interpolated String Handler(C# 10+)とロギング最適化
C# 10以降は「補間文字列ハンドラ」により、API側が補間を受け取って必要時のみ構築する最適化が可能です。
たとえばMicrosoft.Extensions.Logging
はログレベルが無効な場合に文字列構築をスキップできます。
// 例: ILogger が補間ハンドラ対応の拡張メソッドを提供
// logger.LogInformation($"User {userId} logged in at {when:O}");
// ログレベルが無効なら $"..." の実体化を抑制してオーバーヘッドを低減
ただし構造化ログ(プロパティとして検索可能なログ)を重視するなら、テンプレート方式を推奨します。
// 構造化ログ推奨パターン
// logger.LogInformation("User {UserId} logged in at {When}", userId, when);
SQL/JSON生成時のセキュリティ:パラメータ化とエンコード
補間は便利ですが、SQLやシリアライズ対象に直接埋め込むとインジェクションの危険があります。
DBアクセスは必ずパラメータ化し、JSON/HTML等はエンコード/シリアライザを使用します。
// 悪い例(SQLインジェクションの危険)
string bad = $"SELECT * FROM Users WHERE Name = '{userInput}'";
// 良い例(パラメータ化)
/*
using var cmd = new SqlCommand("SELECT * FROM Users WHERE Name = @name", conn);
cmd.Parameters.AddWithValue("@name", userInput);
*/
JSONはSystem.Text.Json
などのシリアライザにオブジェクトを渡すのが安全・確実です。
よくある落とし穴・注意点
$ と @ の順番:$@”…” と @$”…” の違い
$@"..."
と@$"..."
は同義で、いずれも「逐語的+補間」です。
動作上の違いはありません。
可読性の観点でチームのスタイルに合わせて統一するとよいです。
NullReferenceやフォーマット例外を避けるコツ
{obj.Property}
でobj
がnull
だとNullReferenceException
になります。
?.
や??
を活用してください。
$"{obj?.Property ?? "N/A"}"
書式指定は対象型に適さない指定でFormatException
が発生します。
たとえば整数に日時の書式や、無効なカスタム書式は避けます。
// $"{1234:Q}" // 無効な数値書式 → 例外の可能性
カルチャ差異による表示ずれ・テストのポイント
数値や日時の既定書式は現在カルチャに依存します。
テストでは以下のいずれかを徹底すると安定します。
FormattableString.Invariant
でカルチャ非依存の表記に固定する。- テスト開始時に
CultureInfo.CurrentCulture
を明示設定する。 - 期待値もカルチャに合わせて生成する(例:
ToString(provider)
を使用)。
また、ユーザー向け画面ではOS/アプリのカルチャ設定に従うのが自然ですが、ログや永続化文字列はInvariantを用いると解析が容易になります。
まとめ
C#の文字列補間($)は、短く読みやすいコードで変数や式を文字列に埋め込む最良の手段です。
{expr,alignment:format}
の基本を押さえ、数値・日時の書式やアライメントを使い分けると表現力が高まります。
逐語的(@$)やraw($”””)との組み合わせでエスケープの煩雑さを解消し、FormattableString.Invariant
でカルチャ差異もコントロールできます。
性能面では少量は補間で十分、高頻度・大量連結はStringBuilder
や補間ハンドラ対応APIを活用するとよいでしょう。
最後に、SQL/JSONでは安全性を最優先し、パラメータ化やシリアライザを用いるのがベストプラクティスです。
文字列補間を軸に、読みやすさ・正確さ・性能をバランス良く満たすコードを書いていきましょう。