【C#】Raw文字列リテラルと@文字列で実現する複数行文字列の書き方・注意点
複数行文字列を扱うなら、最も手軽なのは"""
で囲むRaw文字列リテラル(C# 11以降)で、改行やエスケープを気にせず記述できます。
従来は先頭に@
を付けるVerbatim文字列で改行を直接入れるか+ Environment.NewLine
で連結していました。
プロジェクトのC#バージョンと可読性を比べて選ぶと良いです。
複数行文字列が必要になる場面
プログラムを書く際に、複数行にわたる文字列を扱う必要が出てくることがあります。
C#では複数行文字列を扱う方法がいくつかありますが、まずはどのような場面で複数行文字列が必要になるのかを具体的に見ていきましょう。
UI定義の埋め込み
アプリケーションのユーザーインターフェース(UI)をコード内に直接埋め込む場合、複数行の文字列が役立ちます。
例えば、XAMLやHTML、あるいはカスタムのUI定義言語を文字列として保持し、動的にUIを生成するケースです。
using System;
class Program
{
static void Main()
{
// HTMLテンプレートを複数行文字列で定義
string htmlTemplate = """
<html>
<head>
<title>サンプルページ</title>
</head>
<body>
<h1>こんにちは、世界!</h1>
<p>これは複数行文字列の例です。</p>
</body>
</html>
""";
Console.WriteLine(htmlTemplate);
}
}
<html>
<head>
<title>サンプルページ</title>
</head>
<body>
<h1>こんにちは、世界!</h1>
<p>これは複数行文字列の例です。</p>
</body>
</html>
このように、UIの定義を複数行文字列で記述すると、コードの可読性が高まり、編集もしやすくなります。
特にHTMLやXAMLのようなマークアップ言語は複数行にわたることが多いため、複数行文字列は非常に便利です。
SQLやJSONのハードコード
データベースアクセスやAPI通信のためにSQLクエリやJSONデータをコード内に直接書き込む場合も、複数行文字列が役立ちます。
SQL文やJSONは構造が複雑で複数行にわたることが多いため、複数行文字列で記述すると見やすくなります。
using System;
class Program
{
static void Main()
{
// SQLクエリを複数行文字列で定義
string sqlQuery = """
SELECT Id, Name, Email
FROM Users
WHERE IsActive = 1
ORDER BY Name ASC
""";
Console.WriteLine(sqlQuery);
// JSONデータを複数行文字列で定義
string jsonData = """
{
"name": "山田太郎",
"age": 30,
"email": "taro.yamada@example.com",
"skills": ["C#", "SQL", "JavaScript"]
}
""";
Console.WriteLine(jsonData);
}
}
SELECT Id, Name, Email
FROM Users
WHERE IsActive = 1
ORDER BY Name ASC
{
"name": "山田太郎",
"age": 30,
"email": "taro.yamada@example.com",
"skills": ["C#", "SQL", "JavaScript"]
}
このようにSQLやJSONを複数行文字列で記述すると、構造がそのまま見えるため、編集やデバッグがしやすくなります。
特にJSONはダブルクォートが多用されるため、エスケープ処理が面倒ですが、Raw文字列リテラルを使うとエスケープ不要で記述できるのが大きなメリットです。
テストデータの記述
ユニットテストや統合テストで、テスト用の文字列データを用意する際にも複数行文字列は便利です。
例えば、ログのサンプルやレスポンスのモックデータ、設定ファイルの内容などを複数行文字列で表現できます。
using System;
class Program
{
static void Main()
{
// ログのサンプルを複数行文字列で定義
string logSample = """
2024-06-01 10:00:00 INFO アプリケーション開始
2024-06-01 10:01:23 WARN 設定ファイルが見つかりません
2024-06-01 10:02:45 ERROR データベース接続失敗
""";
Console.WriteLine(logSample);
}
}
2024-06-01 10:00:00 INFO アプリケーション開始
2024-06-01 10:01:23 WARN 設定ファイルが見つかりません
2024-06-01 10:02:45 ERROR データベース接続失敗
テストデータを複数行文字列で記述すると、テストコードがシンプルになり、テストケースの意図がわかりやすくなります。
特にログや設定ファイルのように複数行にわたるテキストを扱う場合は、複数行文字列が最適です。
このように、UI定義の埋め込み、SQLやJSONのハードコード、テストデータの記述といった場面で複数行文字列は非常に役立ちます。
C#で使える複数行文字列の種類
C#で複数行の文字列を扱う方法は主に3つあります。
Raw文字列リテラル、Verbatim文字列リテラル(@文字列)、そして通常の文字列に改行コードを組み合わせる方法です。
それぞれの特徴や使い方を詳しく見ていきましょう。
Raw文字列リテラル
基本構文
Raw文字列リテラルはC# 11から導入された機能で、複数行の文字列をそのまま記述できる新しい書き方です。
3つ以上の連続したダブルクォート("""
)で囲み、その中に改行や特殊文字をエスケープせずに書けます。
using System;
class Program
{
static void Main()
{
string rawString = """
これはRaw文字列リテラルの例です。
複数行にわたるテキストをそのまま書けます。
"ダブルクォート"もエスケープ不要です。
""";
Console.WriteLine(rawString);
}
}
これはRaw文字列リテラルの例です。
複数行にわたるテキストをそのまま書けます。
"ダブルクォート"もエスケープ不要です。
このように、Raw文字列リテラルは改行やダブルクォートを気にせずに書けるため、JSONやHTMLなどの複雑な文字列を扱うのに便利です。
引用符とハッシュの関係
Raw文字列リテラルの開始と終了は3つ以上の連続したダブルクォートで囲みますが、文字列内に連続したダブルクォートが含まれる場合は、囲むダブルクォートの数を増やす必要があります。
さらに、ハッシュ#
を使って囲む方法もあります。
例えば、文字列内に"""
が含まれる場合は、囲みを4つのダブルクォートに増やします。
string example = """"
これは4つのダブルクォートで囲んだRaw文字列です。
文字列内に"""が含まれても問題ありません。
"""";
また、ハッシュを使うと、囲みのダブルクォートの数を増やさずに済みます。
ハッシュの数は囲むダブルクォートの数と対応しており、文字列の開始と終了に同じ数のハッシュを付けます。
string hashExample = ##"""
文字列内に"""があっても
ハッシュを使うことで安全に囲めます。
"""のような文字列もOKです。
"""##;
この方法は、文字列内に複雑な引用符が含まれる場合に役立ちます。
末尾空白と改行の扱い
Raw文字列リテラルでは、文字列の先頭と末尾の改行や空白の扱いに注意が必要です。
文字列の開始のダブルクォートの直後に改行がある場合、その改行は文字列に含まれません。
逆に、文字列の終了のダブルクォートの直前の改行は含まれます。
また、文字列の各行の先頭にある共通の空白は自動的に除去されます。
これにより、コードのインデントを保ちながら文字列の内容を整形できます。
string indentedRaw = """
これはインデントされたRaw文字列です。
先頭の共通空白は自動的に除去されます。
行の途中の空白はそのまま残ります。
""";
Console.WriteLine(indentedRaw);
これはインデントされたRaw文字列です。
先頭の共通空白は自動的に除去されます。
行の途中の空白はそのまま残ります。
この機能により、コードの見た目を崩さずに文字列を記述できます。
Verbatim文字列リテラル(@)
シンタックスの特徴
Verbatim文字列リテラルは、文字列の先頭に@
を付けて記述します。
改行やタブなどの特殊文字をエスケープせずにそのまま書けるため、複数行の文字列を扱うのに便利です。
string verbatimString = @"
これはVerbatim文字列リテラルの例です。
複数行にわたるテキストをそのまま書けます。
""ダブルクォート""は2つ連続で書きます。
";
Console.WriteLine(verbatimString);
これはVerbatim文字列リテラルの例です。
複数行にわたるテキストをそのまま書けます。
"ダブルクォート"は2つ連続で書きます。
先頭の改行も文字列に含まれるため、必要に応じて調整が必要です。
改行挿入と表示結果
Verbatim文字列リテラルでは、文字列内の改行はそのまま文字列に含まれます。
コードの改行がそのまま出力されるため、複数行のテキストを自然に表現できます。
ただし、文字列の先頭や末尾に不要な改行が入ることがあるため、必要に応じて文字列の先頭や末尾の改行を削除する処理を行うこともあります。
二重引用符のエスケープ方法
Verbatim文字列リテラル内でダブルクォートを表現するには、ダブルクォートを2つ連続で書きます。
これがエスケープの方法です。
string quoteExample = @"彼は言いました。""こんにちは、世界!""";
Console.WriteLine(quoteExample);
彼は言いました。"こんにちは、世界!"
この方法はRaw文字列リテラルと異なり、ダブルクォートを含む文字列を記述する際に少し手間がかかります。
通常の文字列と改行コード
文字列連結とEnvironment.NewLine
通常の文字列リテラルは1行で記述されるため、複数行の文字列を作るには改行コードを明示的に挿入する必要があります。
Environment.NewLine
を使うと、実行環境に応じた改行コードを挿入できます。
string normalString = "これは1行目の文字列です。" + Environment.NewLine +
"これは2行目の文字列です。" + Environment.NewLine +
"これは3行目の文字列です。";
Console.WriteLine(normalString);
これは1行目の文字列です。
これは2行目の文字列です。
これは3行目の文字列です。
この方法はどのC#バージョンでも使えますが、文字列が長くなると可読性が下がることがあります。
\nと\r\nの違い
改行コードには主に2種類あります。
\n
(LF: Line Feed)と\r\n
(CR+LF: Carriage Return + Line Feed)です。
Windows環境では\r\n
が標準ですが、LinuxやmacOSでは\n
が使われます。
string lfString = "1行目\n2行目\n3行目";
string crlfString = "1行目\r\n2行目\r\n3行目";
Console.WriteLine("LF改行:");
Console.WriteLine(lfString);
Console.WriteLine("CR+LF改行:");
Console.WriteLine(crlfString);
LF改行:
1行目
2行目
3行目
CR+LF改行:
1行目
2行目
3行目
多くの環境で\n
だけでも問題なく表示されますが、ファイルの互換性や外部システムとの連携を考慮すると、Environment.NewLine
を使うのが安全です。
Raw文字列リテラルの書き方
クォート数の選択ルール
Raw文字列リテラルは、3つ以上の連続したダブルクォート("""
)で囲むことで表現します。
基本的には3つのダブルクォートで囲めば問題ありませんが、文字列の中に連続したダブルクォートが含まれる場合は、囲むダブルクォートの数を増やす必要があります。
例えば、文字列内に"""
が含まれている場合は、囲みを4つのダブルクォートに増やします。
string example = """"
これは4つのダブルクォートで囲んだRaw文字列です。
文字列内に"""が含まれても問題ありません。
"""";
Console.WriteLine(example);
これは4つのダブルクォートで囲んだRaw文字列です。
文字列内に"""が含まれても問題ありません。
さらに、文字列内に""""
のように4つ以上の連続したダブルクォートが含まれる場合は、囲むダブルクォートの数をそれ以上に増やす必要があります。
囲むクォート数は、文字列内の最大連続ダブルクォート数より1つ多くするのがルールです。
また、ハッシュ#
を使って囲む方法もあります。
ハッシュの数は囲むダブルクォートの数と対応しており、文字列の開始と終了に同じ数のハッシュを付けます。
これにより、囲むダブルクォートの数を増やさずに済み、複雑な引用符を含む文字列も安全に記述できます。
string hashExample = ##"""
文字列内に"""があっても
ハッシュを使うことで安全に囲めます。
"""のような文字列もOKです。
"""##;
Console.WriteLine(hashExample);
文字列内に"""があっても
ハッシュを使うことで安全に囲めます。
"""のような文字列もOKです。
インテンドの自動削除方法
Raw文字列リテラルでは、コードのインデントを保ちながら文字列の内容を整形できるように、共通の先頭空白(インデント)が自動的に削除されます。
これにより、コードの見た目を崩さずに文字列を記述できます。
共通先頭空白の除去
文字列の各行の先頭にある共通の空白は、Raw文字列リテラルの開始位置に合わせて自動的に除去されます。
例えば、以下のコードでは4つのスペースが共通の先頭空白として認識され、文字列から除去されます。
string indentedRaw = """
これはインデントされたRaw文字列です。
先頭の共通空白は自動的に除去されます。
行の途中の空白はそのまま残ります。
""";
Console.WriteLine(indentedRaw);
これはインデントされたRaw文字列です。
先頭の共通空白は自動的に除去されます。
行の途中の空白はそのまま残ります。
ただし、行によって先頭空白の数が異なる場合は、最も少ない空白数が共通空白として除去されます。
空白がない行があれば、その行は除去対象の空白がありませんので、他の行の空白も除去されません。
この機能により、コードのインデントを保ちながら文字列の見た目を整えることができます。
文字列補間との併用
Raw文字列リテラルは文字列補間とも組み合わせて使えます。
文字列補間を使うことで、変数の値を文字列内に埋め込むことが可能です。
変数展開時の$”””記法
Raw文字列リテラルで文字列補間を使う場合は、開始のダブルクォートの前に$
を付けて$"""
のように記述します。
これにより、{}
で囲んだ部分に変数や式の値が展開されます。
using System;
class Program
{
static void Main()
{
string name = "山田太郎";
int age = 30;
string interpolatedRaw = $"""
名前: {name}
年齢: {age}歳
""";
Console.WriteLine(interpolatedRaw);
}
}
名前: 山田太郎
年齢: 30歳
このように、Raw文字列リテラルの中で変数を自然に埋め込めるため、テンプレート文字列としても使いやすいです。
フォーマット指定子との組み合わせ
文字列補間内では、フォーマット指定子も使えます。
例えば、数値の書式や日付のフォーマットを指定して埋め込むことが可能です。
using System;
class Program
{
static void Main()
{
DateTime now = DateTime.Now;
double price = 1234.5678;
string formattedRaw = $"""
現在日時: {now:yyyy/MM/dd HH:mm:ss}
価格: {price:F2}円
""";
Console.WriteLine(formattedRaw);
}
}
現在日時: 2024/06/01 12:34:56
価格: 1234.57円
フォーマット指定子を使うことで、数値や日時の表示を自由にカスタマイズでき、より実用的な文字列を作成できます。
Verbatim文字列での複数行表現
改行挿入の自動 vs 明示
Verbatim文字列リテラル(@
文字列)では、文字列内に記述した改行がそのまま文字列に含まれます。
つまり、コード上で改行を入れれば自動的に改行が挿入されるため、複数行の文字列を自然に表現できます。
using System;
class Program
{
static void Main()
{
string verbatim = @"これは1行目です。
これは2行目です。
これは3行目です。";
Console.WriteLine(verbatim);
}
}
これは1行目です。
これは2行目です。
これは3行目です。
一方で、改行を明示的に入れたい場合は、通常の文字列リテラルのように\n
やEnvironment.NewLine
を使うことも可能ですが、Verbatim文字列では改行を直接書けるため、あえて明示的に改行コードを入れる必要はあまりありません。
ただし、文字列の途中で改行を入れたくない場合や、改行コードを文字列の中に含めたくない場合は、Verbatim文字列を使わずに通常の文字列リテラルで改行コードを制御することが多いです。
エスケープが必要なケース
Verbatim文字列リテラルは多くの特殊文字をエスケープせずに書けますが、唯一エスケープが必要なのはダブルクォート"
です。
ダブルクォートを文字列内に含めたい場合は、2つ連続で書く必要があります。
using System;
class Program
{
static void Main()
{
string quote = @"彼は言いました。""こんにちは、世界!""";
Console.WriteLine(quote);
}
}
彼は言いました。"こんにちは、世界!"
このように、ダブルクォート以外のバックスラッシュや改行はそのまま文字列に含まれますが、ダブルクォートだけは2つ重ねて書く必要がある点に注意してください。
先頭インデントの揃え方
Verbatim文字列リテラルはコード内で複数行にわたって記述できる反面、文字列の先頭にあるインデント(空白やタブ)もそのまま文字列に含まれます。
これにより、コードのインデントを揃えたい場合に文字列の先頭に不要な空白が入ってしまうことがあります。
using System;
class Program
{
static void Main()
{
string indented = @"
これはインデントされた文字列です。
先頭の空白も含まれます。";
Console.WriteLine(indented);
}
}
これはインデントされた文字列です。
先頭の空白も含まれます。
この例では、文字列の先頭に空行とインデントの空白が含まれているため、出力結果にも空白が表示されます。
これを防ぐには、文字列の開始位置を左端に揃えるか、文字列の先頭と末尾の不要な空白や改行を手動で削除する必要があります。
using System;
class Program
{
static void Main()
{
string trimmed = @"これはインデントを揃えた文字列です。
先頭の空白は含まれません。";
Console.WriteLine(trimmed);
}
}
これはインデントを揃えた文字列です。
先頭の空白は含まれません。
また、C#の標準機能としてVerbatim文字列のインデントを自動で除去する仕組みはないため、必要に応じてTrimStart()
やTrim()
メソッドを使って空白を削除することもあります。
string indented = @"
これはインデントされた文字列です。
先頭の空白も含まれます。".TrimStart();
Console.WriteLine(indented);
このように、Verbatim文字列リテラルで複数行文字列を扱う際は、インデントの扱いに注意が必要です。
コードの可読性と文字列の内容のバランスを考慮して使い分けることが大切です。
比較: Raw文字列 vs Verbatim文字列
可読性
Raw文字列リテラルは、複数行の文字列をそのまま書けるため、特にJSONやHTML、SQLなどの構造化されたテキストをコード内に埋め込む際に非常に見やすくなります。
改行やダブルクォートをエスケープする必要がなく、インデントも自動的に調整されるため、コードの可読性が高まります。
一方、Verbatim文字列リテラルは改行をそのまま含められますが、ダブルクォートを2つ連続で書く必要があり、複雑な文字列では視認性がやや落ちることがあります。
また、インデントも文字列に含まれるため、コードの見た目と文字列の内容が一致しない場合があります。
比較項目 | Raw文字列リテラル | Verbatim文字列リテラル |
---|---|---|
改行の扱い | そのまま記述可能 | そのまま記述可能 |
ダブルクォート | エスケープ不要(囲みのクォート数調整) | 2つ連続で書く必要あり |
インデント | 共通先頭空白を自動除去 | 文字列に含まれる |
可読性 | 高い(特に複雑な文字列で効果的) | 普通(エスケープが増えると見づらい) |
エスケープコスト
Raw文字列リテラルは、基本的にエスケープが不要です。
ダブルクォートが文字列内に含まれる場合でも、囲むクォートの数を増やすかハッシュを使うことで対応できるため、エスケープ処理の手間がほとんどありません。
対して、Verbatim文字列リテラルではダブルクォートを2つ連続で書く必要があり、文字列内に多くのダブルクォートがある場合はエスケープコストが高くなります。
バックスラッシュはエスケープ不要ですが、ダブルクォートのエスケープが煩雑になることがあります。
// Verbatim文字列でのダブルクォートエスケープ例
string verbatim = @"彼は言った。""こんにちは""と。";
// Raw文字列リテラルならエスケープ不要
string raw = """
彼は言った。"こんにちは"と。
""";
このように、Raw文字列リテラルはエスケープコストが低く、特にダブルクォートが多い文字列に向いています。
C#バージョン依存性
Raw文字列リテラルはC# 11(.NET 7以降)で導入された新機能です。
そのため、古いC#バージョンや.NET Frameworkでは利用できません。
最新の開発環境が必要になるため、プロジェクトの環境によっては使えない場合があります。
一方、Verbatim文字列リテラルはC# 2.0から存在する機能で、ほぼすべてのC#環境で利用可能です。
互換性を重視する場合はVerbatim文字列リテラルが無難です。
機能 | 利用可能なC#バージョン |
---|---|
Raw文字列リテラル | C# 11以降(.NET 7以降) |
Verbatim文字列リテラル | C# 2.0以降(ほぼ全環境) |
ファイルサイズとIL生成
Raw文字列リテラルとVerbatim文字列リテラルは、どちらもコンパイル時に文字列リテラルとして埋め込まれます。
IL(中間言語)上の文字列データとしてはほぼ同じ扱いになるため、ファイルサイズや実行時のパフォーマンスに大きな差はありません。
ただし、Raw文字列リテラルは複数行の文字列をそのまま記述できるため、コードの行数が減り、ソースコードの可読性や保守性が向上します。
これにより、間接的に開発効率が上がるメリットがあります。
また、Verbatim文字列リテラルはエスケープのために文字数が増えることがあり、ソースコードのサイズがやや大きくなる場合がありますが、コンパイル後のILサイズにはほとんど影響しません。
項目 | Raw文字列リテラル | Verbatim文字列リテラル |
---|---|---|
ソースコードの行数 | 少なくできる(複雑な文字列で顕著) | 多くなることがある |
ILサイズ | 同等 | 同等 |
実行時パフォーマンス | 同等 | 同等 |
総じて、Raw文字列リテラルは最新環境での開発において、可読性とエスケープコストの面で優れています。
一方、Verbatim文字列リテラルは互換性が高く、古い環境でも安心して使える選択肢です。
よくあるハマりどころ
行末スペースの差異
Raw文字列リテラルやVerbatim文字列リテラルを使う際に、行末のスペースが意図せず文字列に含まれてしまうことがあります。
特にRaw文字列リテラルでは、行末の空白は文字列の一部として扱われるため、不要なスペースが入ると文字列の比較や表示に影響を与えることがあります。
string rawWithSpaces = """
これは行末にスペースがある行です。
これはスペースなしの行です。
""";
Console.WriteLine($"[{rawWithSpaces}]");
[これは行末にスペースがある行です。
これはスペースなしの行です。
]
この例では、1行目の末尾にスペースが3つ入っているため、出力にもそのまま反映されます。
意図しない空白が混入すると、文字列の比較で不一致になるなどのトラブルが起きやすいので注意が必要です。
対策としては、エディタの設定で行末の空白を可視化したり、自動削除する機能を使うことが有効です。
また、文字列の後処理でTrimEnd()
を使って末尾の空白を削除する方法もあります。
インデントがコードにも残る
Verbatim文字列リテラルでは、コード内のインデントがそのまま文字列に含まれます。
これにより、コードの可読性を保つためにインデントを付けると、文字列の先頭に不要な空白が入ってしまうことがあります。
string verbatimIndented = @"
これはインデントされた文字列です。
先頭の空白も含まれます。";
Console.WriteLine(verbatimIndented);
これはインデントされた文字列です。
先頭の空白も含まれます。
このように、先頭に空行や空白が入るため、意図しないフォーマットになることがあります。
対策としては、文字列の開始位置を左端に揃えるか、TrimStart()
やTrim()
メソッドで不要な空白を削除することが挙げられます。
Raw文字列リテラルでは共通の先頭空白が自動的に除去されるため、この問題は起きにくいですが、Verbatim文字列では注意が必要です。
ダブルクォートが多いJSON文字列
JSON文字列はダブルクォートが多用されるため、Verbatim文字列リテラルで記述するとダブルクォートを2つ連続で書く必要があり、可読性が大きく低下します。
string jsonVerbatim = @"{
""name"": ""山田太郎"",
""age"": 30,
""skills"": [""C#"", ""SQL"", ""JavaScript""]
}";
Console.WriteLine(jsonVerbatim);
{
"name": "山田太郎",
"age": 30,
"skills": ["C#", "SQL", "JavaScript"]
}
このように、ダブルクォートのエスケープが多くなると、編集ミスや見落としが発生しやすくなります。
Raw文字列リテラルを使うとエスケープ不要で記述できるため、JSONのような文字列にはRaw文字列リテラルが適しています。
テキストエディタのフォーマット干渉
複数行文字列をコード内に記述する際、テキストエディタやIDEの自動フォーマット機能が意図せず文字列のインデントや改行を変更してしまうことがあります。
これにより、文字列の内容が変わってしまい、動作に影響を与える場合があります。
例えば、Raw文字列リテラルのインデントを自動で揃えようとしたり、行末の空白を削除したりする設定が有効になっていると、文字列のフォーマットが崩れることがあります。
対策としては、以下の方法が考えられます。
- エディタの自動フォーマット機能を文字列リテラル内では無効にする設定を行う
- 文字列リテラルの開始位置を左端に揃えて、インデントの影響を受けにくくする
- 重要な文字列は外部ファイルに分離し、コードからは読み込む形にする
これらの対策により、文字列のフォーマットが意図せず変わるリスクを減らせます。
特にチーム開発では、エディタの設定を統一することも重要です。
テスト駆動で書くときのポイント
スナップショットテストとの相性
スナップショットテストは、テスト対象の出力や状態をファイルや文字列として保存し、将来のテスト実行時にその内容と比較する手法です。
複数行の文字列を扱うことが多いため、Raw文字列リテラルやVerbatim文字列リテラルはスナップショットテストと非常に相性が良いです。
Raw文字列リテラルを使うと、テストコード内にスナップショットの期待値をそのまま自然な形で記述でき、改行やインデントも保持されるため、期待値の可読性が高まります。
using System;
class Program
{
static void Main()
{
string expectedJson = """
{
"name": "山田太郎",
"age": 30,
"skills": ["C#", "SQL", "JavaScript"]
}
""";
// ここで実際の出力とexpectedJsonを比較するテストコードを書く想定
Console.WriteLine(expectedJson);
}
}
{
"name": "山田太郎",
"age": 30,
"skills": ["C#", "SQL", "JavaScript"]
}
このように、スナップショットの内容をRaw文字列リテラルで記述すると、テストコードがシンプルでわかりやすくなります。
Verbatim文字列リテラルでも同様に記述可能ですが、ダブルクォートのエスケープが必要になるため、Raw文字列リテラルのほうがより扱いやすいです。
また、スナップショットファイルを外部に保存する場合でも、テストコード内の期待値をRaw文字列リテラルで表現しておくと、テストの意図が明確になります。
Assertにそのまま埋め込む場合
ユニットテストのAssert
メソッドに複数行文字列を直接埋め込むケースも多いです。
この場合、Raw文字列リテラルやVerbatim文字列リテラルを使うことで、期待値の文字列を見やすく記述できます。
using System;
using NUnit.Framework;
[TestFixture]
public class SampleTests
{
[Test]
public void Test_MultiLineString()
{
string actual = GetSampleOutput();
Assert.AreEqual("""
これは期待される
複数行の文字列です。
改行やインデントも含みます。
""", actual);
}
private string GetSampleOutput()
{
return "これは期待される\n複数行の文字列です。\n改行やインデントも含みます。\n";
}
}
このように、期待値をRaw文字列リテラルで直接書くと、改行や空白をエスケープせずに済み、テストコードの可読性が向上します。
Verbatim文字列リテラルでも同様に書けますが、ダブルクォートのエスケープに注意が必要です。
また、複雑な文字列や長いテキストをAssertに埋め込む場合は、テストコードが煩雑になりやすいため、外部ファイルに期待値を分離して読み込む方法も検討すると良いでしょう。
このように、複数行文字列リテラルを活用することで、テストコードの保守性と可読性を高められます。
セキュリティと可搬性
改行コードによるプラットフォーム差
複数行文字列を扱う際に注意したいのが、改行コードの違いによるプラットフォーム間の差異です。
Windowsでは改行コードが\r\n
(CR+LF)ですが、LinuxやmacOSでは\n
(LF)のみが使われます。
この違いは、文字列リテラル内の改行にも影響を与ります。
Raw文字列リテラルやVerbatim文字列リテラルで改行をそのまま記述すると、ソースコードの改行コードがそのまま文字列に含まれます。
つまり、開発環境のOSやエディタの設定によって改行コードが異なる場合、実行時の文字列の改行コードも変わってしまう可能性があります。
この差異は、ファイル出力や外部システムとの連携、文字列比較などで問題になることがあります。
例えば、Windowsで作成した文字列をLinux環境で処理すると、改行コードの違いで不具合が発生することがあります。
対策としては、改行コードを明示的にEnvironment.NewLine
で統一したり、文字列の改行コードを実行時に正規化する方法があります。
Raw文字列リテラルを使う場合でも、改行コードの扱いに注意し、必要に応じて後処理で改行コードを変換することが望ましいです。
パス文字列とエスケープ
ファイルパスやURLなどの文字列をコード内に直接書く場合、エスケープ処理が重要になります。
特にWindowsのファイルパスはバックスラッシュ\
を区切り文字として使うため、通常の文字列リテラルではエスケープが必要です。
string path = "C:\\Users\\Public\\Documents\\file.txt";
Verbatim文字列リテラルを使うと、バックスラッシュをエスケープせずにそのまま書けるため、パス文字列の記述が楽になります。
string path = @"C:\Users\Public\Documents\file.txt";
Raw文字列リテラルでも同様にバックスラッシュをエスケープ不要で書けますが、囲みのクォート数やハッシュの使い方に注意が必要です。
ただし、パス文字列にユーザー入力が混ざる場合は、エスケープ処理だけでなく、パスの正規化や検証を行わないと、パス・トラバーサル攻撃などのセキュリティリスクが生じます。
文字列リテラルの扱いに加え、ユーザー入力の安全性を確保することが重要です。
ユーザー入力の混在リスク
コード内で複数行文字列リテラルを使う場合でも、ユーザーからの入力や外部データと混在させるケースがあります。
このとき、文字列の内容に予期しない改行や特殊文字が含まれると、SQLインジェクションやクロスサイトスクリプティング(XSS)などの脆弱性につながる恐れがあります。
例えば、Raw文字列リテラルでSQLクエリを記述しつつ、ユーザー入力を文字列補間で埋め込む場合、入力値のエスケープやパラメータ化が必須です。
string userInput = "abc'; DROP TABLE Users; --";
string sql = $"""
SELECT * FROM Users WHERE Name = '{userInput}'
""";
このままだとSQLインジェクションのリスクがあります。
必ずパラメータ化クエリを使うか、入力値を適切にサニタイズしてください。
また、HTMLやJavaScriptのテンプレート文字列にユーザー入力を埋め込む場合も、エスケープ処理を怠るとXSS攻撃の原因になります。
複数行文字列リテラルは便利ですが、ユーザー入力を直接埋め込む際は、必ず適切なエスケープや検証を行い、セキュリティリスクを回避することが重要です。
実運用での選択基準
ランタイム要件
複数行文字列リテラルの選択において、まず考慮すべきはランタイム環境の要件です。
Raw文字列リテラルはC# 11以降で導入された機能であり、.NET 7以降の環境が必要です。
そのため、古い.NET Frameworkや.NET Coreのバージョンを使っているプロジェクトでは利用できません。
一方、Verbatim文字列リテラル(@
文字列)はC# 2.0から存在し、ほぼすべての.NET環境でサポートされています。
したがって、レガシー環境や幅広い互換性が求められる場合はVerbatim文字列リテラルを選択するのが無難です。
また、ランタイムの制約だけでなく、実行環境のOSやプラットフォームによって改行コードの扱いが異なるため、複数行文字列の改行コードを明示的に管理する必要がある場合もあります。
これらの要件を踏まえて、どの文字列リテラルを使うか判断してください。
チームのC#バージョン統一度
チーム開発においては、メンバー全員が同じC#バージョンを使っているかどうかも重要な選択基準です。
Raw文字列リテラルは比較的新しい機能であるため、チーム内でC# 11を使っていないメンバーがいる場合、コードの互換性やビルドエラーの原因になります。
そのため、チームのC#バージョンが統一されていない場合や、複数のプロジェクトで異なるバージョンを使っている場合は、Verbatim文字列リテラルを使うほうが安全です。
逆に、全員が最新のC#環境を使っているなら、Raw文字列リテラルを積極的に活用してコードの可読性を高めることができます。
また、CI/CD環境やビルドサーバーのC#バージョンも合わせて確認し、チーム全体で統一した環境を整備することが望ましいです。
静的解析ツールの対応
静的解析ツールやコードフォーマッターの対応状況も、複数行文字列リテラルの選択に影響します。
Raw文字列リテラルは比較的新しい構文のため、一部の静的解析ツールやフォーマッターが完全に対応していない場合があります。
対応していないツールを使うと、誤検知や不要な警告が発生したり、コードフォーマットが崩れたりすることがあります。
これにより、開発効率が低下したり、コード品質の維持が難しくなる可能性があります。
Verbatim文字列リテラルは長年使われている構文なので、ほとんどの静的解析ツールやフォーマッターで問題なくサポートされています。
したがって、チームで使用している静的解析ツールやフォーマッターの対応状況を確認し、Raw文字列リテラルを使う場合はツールのアップデートや設定調整が必要かどうかを検討してください。
対応が不十分な場合は、Verbatim文字列リテラルを選択するほうが安定した開発環境を維持できます。
他言語との類似機能比較
PythonのTriple Quote
Pythonでは、複数行文字列を記述するためにトリプルクォート'''
または """
を使います。
トリプルクォートで囲むことで、改行や空白を含む文字列をそのまま記述でき、エスケープの手間が少なくなります。
text = """
これはPythonの
複数行文字列です。
"ダブルクォート"もそのまま使えます。
"""
print(text)
これはPythonの
複数行文字列です。
"ダブルクォート"もそのまま使えます。
PythonのトリプルクォートはRaw文字列リテラルのようにインデントの自動除去機能はありませんが、textwrap.dedent
関数を使うことでインデントを調整できます。
C#のRaw文字列リテラルと似た使い勝手ですが、囲みのクォート数は固定で、囲みの増減によるエスケープ回避はできません。
JavaScriptのTemplate Literal
JavaScriptではバッククォート`
を使ったテンプレートリテラルが複数行文字列を表現します。
改行をそのまま含められ、文字列補間も${}
で簡単に行えます。
const name = "太郎";
const text = `
こんにちは、${name}さん。
これはJavaScriptの
複数行文字列です。
`;
console.log(text);
こんにちは、太郎さん。
これはJavaScriptの
複数行文字列です。
JavaScriptのテンプレートリテラルはC#のRaw文字列リテラルの文字列補間に近い機能を持ちますが、囲みはバッククォート1種類のみで、囲みの増減によるエスケープ回避はできません。
インデントの自動除去も標準ではありません。
JavaのText Block
Java 13以降で導入されたText Blockは、3つのダブルクォート("""
)で囲むことで複数行文字列を表現します。
改行や空白をそのまま含められ、インデントの自動除去機能も備えています。
String text = """
これはJavaの
Text Blockです。
"ダブルクォート"もそのまま使えます。
""";
System.out.println(text);
これはJavaの
Text Blockです。
"ダブルクォート"もそのまま使えます。
JavaのText BlockはC#のRaw文字列リテラルと非常に似ており、囲みのクォート数は固定ですが、インデントの自動除去や改行の扱いが共通しています。
エスケープの手間が少なく、複雑な文字列を扱いやすい点が特徴です。
KotlinのRaw String
Kotlinでは3つのダブルクォート("""
)で囲むRaw Stringを使って複数行文字列を表現します。
改行や空白をそのまま含められ、エスケープ不要で記述可能です。
val text = """
これはKotlinの
Raw Stringです。
"ダブルクォート"もそのまま使えます。
""".trimIndent()
println(text)
これはKotlinの
Raw Stringです。
"ダブルクォート"もそのまま使えます。
KotlinのRaw Stringはインデントの自動除去機能trimIndent()
やtrimMargin()
を使うことで、コードのインデントを保ちながら文字列の見た目を整えられます。
C#のRaw文字列リテラルと似た使い勝手で、エスケープの手間がほとんどありません。
これらの言語の複数行文字列機能は、いずれも改行や空白をそのまま扱える点で共通していますが、囲みの方法やインデントの自動除去、文字列補間の仕組みなどに違いがあります。
C#のRaw文字列リテラルは、これらの言語の良い部分を取り入れたモダンな機能と言えます。
周辺機能との統合
Source Generatorでの利用
C#のSource Generatorは、コンパイル時にコードを自動生成する機能です。
Raw文字列リテラルは複数行の文字列をそのまま記述できるため、Source Generatorでテンプレートやコードスニペットを埋め込む際に非常に便利です。
例えば、Source Generator内でHTMLやSQLのテンプレートをRaw文字列リテラルとして保持し、生成コードに埋め込むことができます。
これにより、複雑な文字列のエスケープ処理を気にせずに済み、コードの可読性と保守性が向上します。
// Source Generatorの一部としてRaw文字列リテラルを使う例
string template = """
<div>
<h1>タイトル</h1>
<p>これはSource Generatorで生成されるHTMLテンプレートです。</p>
</div>
""";
また、文字列補間と組み合わせることで、動的なコード生成も柔軟に行えます。
Source Generatorのコード内でRaw文字列リテラルを活用すると、生成されるコードの品質が高まり、開発効率が向上します。
Razorページのインライン書式
ASP.NET CoreのRazorページでは、HTMLとC#コードを混在させて記述します。
複数行のHTMLやJavaScriptをインラインで埋め込む際に、Raw文字列リテラルやVerbatim文字列リテラルが役立ちます。
特に、JavaScriptのテンプレートやCSSのスタイルをC#コード内で保持し、Razorページに埋め込む場合、Raw文字列リテラルを使うとエスケープの手間が減り、コードがすっきりします。
@{
var script = """
<script>
alert("こんにちは、世界!");
</script>
""";
}
@Html.Raw(script)
このように、RazorページのコードビハインドやViewModelでRaw文字列リテラルを使うことで、複数行のHTMLやスクリプトを簡潔に管理できます。
Verbatim文字列リテラルでも可能ですが、Raw文字列リテラルのほうがエスケープ不要で扱いやすいです。
JSONリソースの埋め込み
アプリケーションでJSON形式の設定やデータをコード内に埋め込む場合、Raw文字列リテラルは非常に便利です。
JSONはダブルクォートが多用されるため、Verbatim文字列リテラルではエスケープが煩雑になりますが、Raw文字列リテラルならそのまま記述できます。
string jsonConfig = """
{
"AppName": "サンプルアプリ",
"Version": "1.0.0",
"Features": {
"Logging": true,
"Caching": false
}
}
""";
Console.WriteLine(jsonConfig);
{
"AppName": "サンプルアプリ",
"Version": "1.0.0",
"Features": {
"Logging": true,
"Caching": false
}
}
このように、JSONリソースをRaw文字列リテラルで埋め込むと、エスケープの手間がなくなり、可読性が向上します。
さらに、JSONのパース処理と組み合わせて、設定のハードコードやテストデータの埋め込みに活用できます。
これらの周辺機能とRaw文字列リテラルの組み合わせにより、複雑な文字列の管理が容易になり、コードの保守性や開発効率が大きく向上します。
参考コードスニペット集
LINQクエリ
複雑なLINQクエリを文字列として保持したい場合、Raw文字列リテラルを使うと可読性が高まります。
特にSQL風のクエリや複数行にわたるクエリ文字列を扱う際に便利です。
using System;
class Program
{
static void Main()
{
string linqQuery = """
from user in users
where user.IsActive && user.Age > 20
orderby user.Name ascending
select new
{
user.Id,
user.Name,
user.Email
}
""";
Console.WriteLine(linqQuery);
}
}
from user in users
where user.IsActive && user.Age > 20
orderby user.Name ascending
select new
{
user.Id,
user.Name,
user.Email
}
このようにRaw文字列リテラルを使うことで、クエリの構造がそのまま見えるため、編集やレビューがしやすくなります。
HTMLテンプレート
HTMLテンプレートをコード内に埋め込む場合もRaw文字列リテラルが役立ちます。
複数行のHTMLをそのまま記述でき、ダブルクォートのエスケープも不要です。
using System;
class Program
{
static void Main()
{
string htmlTemplate = """
<html>
<head>
<title>サンプルページ</title>
</head>
<body>
<h1>こんにちは、世界!</h1>
<p>これはRaw文字列リテラルを使ったHTMLテンプレートです。</p>
</body>
</html>
""";
Console.WriteLine(htmlTemplate);
}
}
<html>
<head>
<title>サンプルページ</title>
</head>
<body>
<h1>こんにちは、世界!</h1>
<p>これはRaw文字列リテラルを使ったHTMLテンプレートです。</p>
</body>
</html>
この方法は、メールテンプレートやWebページの静的部分をコード内に埋め込む際に特に便利です。
設定ファイル生成
アプリケーションの設定ファイル(例えばJSONやXML)をコードから生成する場合、Raw文字列リテラルを使うとエスケープの手間が省け、可読性が向上します。
using System;
class Program
{
static void Main()
{
string jsonConfig = """
{
"AppName": "サンプルアプリ",
"Version": "1.0.0",
"Logging": {
"Level": "Info",
"FilePath": "logs/app.log"
}
}
""";
Console.WriteLine(jsonConfig);
}
}
{
"AppName": "サンプルアプリ",
"Version": "1.0.0",
"Logging": {
"Level": "Info",
"FilePath": "logs/app.log"
}
}
このように、設定ファイルのテンプレートをRaw文字列リテラルで記述すると、編集や管理がしやすくなり、テストデータの作成や初期設定の埋め込みに役立ちます。
トラブルシューティング
コンパイルエラーCS1012への対処
コンパイルエラーCS1012は「文字列リテラルが正しく終了していません」という意味で、Raw文字列リテラルやVerbatim文字列リテラルを使う際に発生しやすいエラーです。
特にRaw文字列リテラルで囲みのダブルクォート数が不足していたり、文字列の終了が正しく認識されていない場合に起こります。
例えば、Raw文字列リテラルの開始に3つのダブルクォートを使ったのに、文字列内に3連続のダブルクォートが含まれていると、コンパイラは文字列の終了を誤認識し、CS1012エラーを出します。
// エラー例
string invalidRaw = """
これは"3つの連続した"""ダブルクォート"を含む文字列です。
""";
この場合の対処法は、囲みのダブルクォート数を増やすか、ハッシュ記号#
を使って囲む方法です。
// 囲みを4つに増やす例
string validRaw = """"
これは"3つの連続した"""ダブルクォート"を含む文字列です。
"""";
または
// ハッシュを使う例
string validRawHash = ##"""
これは"3つの連続した"""ダブルクォート"を含む文字列です。
"""##;
このように、囲みのダブルクォート数を文字列内の最大連続ダブルクォート数より1つ多くするか、ハッシュを使って囲むことでCS1012エラーを回避できます。
Verbatim文字列リテラルでCS1012が出る場合は、文字列の開始と終了の@""
のペアが正しく対応しているか、ダブルクォートのエスケープが正しいかを確認してください。
識別子誤解読とカラーリング
Raw文字列リテラルやVerbatim文字列リテラルを使う際、IDEやエディタのシンタックスハイライト(カラーリング)が正しく動作しないことがあります。
特にRaw文字列リテラルは新しい構文のため、古いエディタや拡張機能では文字列の開始・終了を誤認識し、識別子やキーワードのカラーリングが崩れることがあります。
例えば、Raw文字列リテラルの囲みが正しく認識されず、文字列の途中からコードとして扱われたり、逆にコードが文字列として扱われたりすることがあります。
これにより、コードの可読性が低下し、編集ミスを誘発する恐れがあります。
対策としては以下の方法があります。
- 最新のIDEやエディタ、拡張機能にアップデートする
- Raw文字列リテラルの囲みを明確にし、インデントや空白を適切に調整する
- 一時的にVerbatim文字列リテラルに切り替えて編集する
Visual StudioやVS Codeの最新バージョンではRaw文字列リテラルのサポートが進んでいますが、プラグインやテーマによっては対応が遅れている場合もあります。
チームで使うエディタの環境を統一し、最新の状態に保つことが重要です。
また、Raw文字列リテラルの開始と終了のダブルクォート数やハッシュの数が一致していないと、カラーリングが崩れる原因になるため、記述ミスがないかも確認してください。
パフォーマンス最適化
コンパイル時フォーマット
Raw文字列リテラルやVerbatim文字列リテラルは、コンパイル時に文字列リテラルとしてIL(中間言語)に埋め込まれます。
そのため、文字列のフォーマットや改行はコンパイル時に確定し、実行時のパフォーマンスにほとんど影響を与えません。
ただし、文字列補間を使う場合は、補間部分の評価が実行時に行われるため、補間式の計算コストがパフォーマンスに影響します。
Raw文字列リテラルの文字列補間$"""
は、コンパイル時にフォーマットが最適化されることもありますが、複雑な式やメソッド呼び出しが含まれると実行時コストが増えます。
パフォーマンスを最適化するためには、可能な限り定数文字列はRaw文字列リテラルとしてコンパイル時に確定させ、動的な部分は必要最低限に抑えることが重要です。
また、文字列のインデントや空白の自動除去はコンパイル時に処理されるため、実行時の余計な処理は発生しません。
これにより、複雑な複数行文字列でも高速に扱えます。
StringBuilderとの分担
大量の文字列連結や動的な文字列生成が必要な場合、StringBuilder
を使うことでパフォーマンスを向上させられます。
Raw文字列リテラルやVerbatim文字列リテラルは静的な文字列を扱うのに適していますが、動的に変化する文字列を効率的に生成するにはStringBuilder
が有効です。
例えば、複数のRaw文字列リテラルを組み合わせて大きな文字列を作る場合、StringBuilder
を使って連結処理を行うと、メモリの再割り当てやコピーを減らせます。
using System;
using System.Text;
class Program
{
static void Main()
{
var sb = new StringBuilder();
sb.Append("""
<html>
<head><title>サンプル</title></head>
<body>
""");
for (int i = 0; i < 3; i++)
{
sb.Append($"""
<p>段落 {i + 1}</p>
""");
}
sb.Append("""
</body>
</html>
""");
string result = sb.ToString();
Console.WriteLine(result);
}
}
<html>
<head><title>サンプル</title></head>
<body>
<p>段落 1</p>
<p>段落 2</p>
<p>段落 3</p>
</body>
</html>
このように、静的な部分はRaw文字列リテラルで記述し、動的に変化する部分はStringBuilder
で組み立てることで、パフォーマンスと可読性のバランスを取れます。
まとめると、Raw文字列リテラルはコンパイル時に最適化されるため静的文字列に最適であり、動的な文字列生成はStringBuilder
に任せるのがパフォーマンス面で効果的です。
用途に応じて使い分けることが重要です。
より扱いやすくするツールチェーン
Visual Studioのコードスニペット
Visual Studioでは、Raw文字列リテラルやVerbatim文字列リテラルを効率よく記述するためにコードスニペットを活用できます。
コードスニペットは定型コードを簡単に挿入できる機能で、複数行文字列のテンプレートを登録しておくと、入力の手間を大幅に削減できます。
例えば、Raw文字列リテラルの基本構文をスニペットとして登録しておくと、rawstr
などのショートカットを入力してTabキーを押すだけで、以下のようなテンプレートが挿入されます。
"""
$END$
"""
このスニペット内で$END$
はカーソルの位置を示し、すぐに文字列の編集を開始できます。
さらに、囲みのダブルクォート数やハッシュの数を変えたい場合は、カスタムスニペットを作成して複数パターンを用意することも可能です。
Visual StudioのスニペットはXML形式で定義され、簡単にインポート・エクスポートできるため、チームで共有して統一したコーディングスタイルを促進できます。
VS Code拡張機能
Visual Studio Code(VS Code)でもRaw文字列リテラルやVerbatim文字列リテラルの記述を支援する拡張機能が多数存在します。
C#用の拡張機能(例: C# for Visual Studio Code by OmniSharp)には、構文のハイライトや補完機能が含まれており、Raw文字列リテラルのサポートも進んでいます。
さらに、ユーザーが独自にスニペットを作成して登録できるため、Visual Studio同様に複数行文字列のテンプレートを素早く挿入できます。
例えば、rawstr
やverbatim
といったトリガーワードでRaw文字列やVerbatim文字列の雛形を呼び出せます。
また、VS Codeの拡張機能には、文字列のインデント調整や改行コードの統一を支援するツールもあり、複数行文字列の編集を快適にします。
これらを組み合わせることで、Raw文字列リテラルの記述ミスやフォーマット崩れを防げます。
ReSharperのインスペクション
JetBrainsのReSharperはC#開発における強力な静的解析ツールで、Raw文字列リテラルやVerbatim文字列リテラルのサポートも充実しています。
ReSharperはコードの品質向上やリファクタリング支援に加え、文字列リテラルの書き方に関するインスペクション(警告や提案)を提供します。
例えば、ReSharperは以下のような支援を行います。
- Raw文字列リテラルへの変換提案
長いVerbatim文字列やエスケープが多い文字列をRaw文字列リテラルに変換するリファクタリングを提案し、可読性向上を促します。
- 不要なエスケープの警告
Verbatim文字列内で不要にエスケープされている箇所を検出し、修正を促します。
- インデントや改行の整形支援
複数行文字列のインデントが不揃いな場合に警告を出し、整形をサポートします。
これらの機能により、Raw文字列リテラルやVerbatim文字列リテラルの記述ミスを減らし、コードの一貫性を保てます。
ReSharperを導入しているチームでは、これらのインスペクションを活用して複数行文字列の品質を高めることが可能です。
まとめ
C#の複数行文字列はRaw文字列リテラルとVerbatim文字列リテラルの2種類があり、それぞれ特徴や使いどころがあります。
Raw文字列リテラルはエスケープ不要で可読性が高く、最新のC#環境で特に有効です。
一方、Verbatim文字列リテラルは互換性が高く、古い環境でも使えます。
用途や環境に応じて使い分け、ツールや静的解析と連携させることで、効率的かつ安全に複数行文字列を扱えます。