string.Formatは旧仕様で問題もアリ。文字列補間に置き換えよう

C#

こんにちは、働くC#プログラマーのさんさめです。

開発されてから長期間保守されている
アプリケーションのソースコードには、
当時の仕様で書かれているコードが一定数存在します。

  • async/awaitがない頃の非同期処理
  • null条件演算子がない頃のnullチェック
  • 出力変数宣言がない頃の出力引数の宣言
  • etc…

上に挙げたものでも色々語れてしまいますが、
今回は割愛します。

そのような旧仕様の中でも、
string.Formatメソッドは大変問題があるので、
積極的に置き換えていくことをおススメします。

本記事では、string.Formatの問題点と、
C#6.0で追加された代替機能である文字列補間($””)の解説をします。

スポンサーリンク

string.Formatとは

string.Formatは
文字列リテラルと、変数や式の値を混合させた、
新しい文字列を作りたいときに使用するメソッドです。

例えば以下のように使います。

var intValue = 42;
var str = string.Format("{0}を2倍した数は{1}です",
    intValue,
    intValue * 2);

Console.WriteLine(str); // 42を2倍した数は84です

{0}や{1}と書いた部分が、インデックスを表しており、
そのあとに続けた引数に置き換わります。

C言語を勉強したことある人は
ピンとくるかもしれませんが、
これはprintf関数の呼び出し方にどことなく似ています。

そのため、
CからC#に移った人は特に違和感なく使いがちです。
使いがち、というか昔はこれしかなかったのですが。

しかしながら、前述したように
C#6.0以降であれば、文字列補間を使った方が、
すっきり明解、かつ問題も起きにくいと良いことづくめになります。

文字列補間とは

文字列補間とは、C#6.0で追加された、
string.Formatと同様のことができる記法です。

先ほどのサンプルコードを文字列補間による記法で
置き換えてみます。

var intValue = 42;
var str = $"{intValue}を2倍した数は{intValue * 2}です";
Console.WriteLine(str); // 42を2倍した数は84です

文字列補間の記法は
文字列リテラルを表す””の前に「$」を入力する
というものになります。

そして、””で括った部分に波括弧「{}」を入力すると、
括弧内は式として認識されます。

どうでしょうか。
既にstring.Formatの時よりも
格段に読みやすいですね。

また、文字列補間で記述した場合には
ハイライトもちゃんと付くため、
読みやすさには歴然の差がでてきます。

string.Formatの問題点と文字列補間での改善点

さて、読みやすさの面だけでなく、
string.Formatにはいくつか問題点があります。

  • タイピング量が多い
  • 文字列と引数の関係性が分かりにくい
  • 使い方を間違えても実行してみるまで気づけない

そして、文字列補間による記法では、
これらすべてを改善しています。
ひとつずつ見ていきましょう。

string.Formatの問題点1:タイピング量が多い

1つ目の問題点はシンプルで、
「タイピング量が多い」
ことです。

「string.Format」と打つだけでも大変なのに、
「()」も必要ですし、引数が多ければ「,」も
つどつど入力しなければいけません。

タイピング量が多いということは文字数も多いので、
読む人の負担にもなります。

文字列補間の書き方であれば、
「$」を先頭につけるくらいで済みます。

また、記法を知らない人が読んだとしても、
文脈で理解できる範疇です。

string.Formatの問題点2:文字列と引数の関係性が分かりにくい

2つ目の問題点は、
「文字列と引数の関係性が分かりにくい」
という点です。

もう一度string.Formatのコードを見てみましょう。

var str = string.Format("{0}を2倍した数は{1}です",
    intValue,
    intValue * 2);

{0}や{1}と、intValueやintValue * 2の部分の対応を把握するのに、
少し時間がかかりますよね。

え、そうでもないですか?
では以下ならどうでしょう。

var str = string.Format("{2}を2倍した数は{0}ですが、{1}と{2}を足すと{3}になります",
    intValue * 2,
    4,
    intValue,
    intValue + 4);
さんさめ
さんさめ

ぎゃあああああ!

インデックスの登場順序をバラバラにした上に、
{2}を2回使ってみました。

さすがにこれは極端な例ですが、
こういうことも書けてしまうわけです。

文字列補間の書き方であれば、
この極端な例も以下の通りです。

$"{intValue}を2倍した数は{intValue * 2}ですが、{4}と{intValue}を足すと{intValue + 4}になります";

少し横に長くなりはしましたが、
非常に分かりやすいですね。

このページで使用しているコードハイライトの性能がイマイチですが、
VisualStudioならこの通りです。

さんさめ
さんさめ

うむ、。安心する~

string.Formatの問題点3:使い方を間違えても実行してみるまで気づけない

さて、最初のサンプルを少しいじって、
以下のように書き換えたとしましょう。

var str = string.Format("{0}を2倍した数は{1}です",
    intValue);

何か足りてませんね。
{1}を指定しているにも関わらず、
そのあとの引数が1つしかありません。

でもこれ、実はビルドは成功するんです。

そして、該当箇所を実行しようとしたときに、
例外になります。

これが3つ目の問題点です。
できればコンパイル時にエラーになってほしいですね。

もし、テスト不足などで
ユーザーにリリースしてから初めて発覚…
とかなったらとっても悲惨です。

文字列補間ではこのようなことはありません。
例えば以下の書き方をしたら、
しっかりコンパイルエラーになります。

$"{intValue}を2倍した数は{}です";
                     // ↑中括弧記述しているのに式がないのでエラー

間違った記述に早めに気づけることで、
ユーザーに不利益を被らせにくくなりますね。

ちなみに、VisualStudio 2019では、
string.Formatの引数不足に対して、
テキストエディタ上で警告を出してくれるようになっています。
賢い。 残念ながらビルドは通ってしまいますが

さんさめは文字列補間を積極的におススメします

string.Formatの3つの問題点を挙げて、
文字列補間の優位性を解説しました。

さんさめ自身も昔はstring.Formatを使っていましたが、
やはり書きづらいし読みづらいしでつらかったですね。

今ではstring.Formatは全く使っていません
文字列補間でサクサクコーディングしています。

というか、string.Formatは
潜在的に実行時エラーの可能性も含んでいるため、
見かけ次第置き換えています。

非常におススメです。
ぜひ置き換えていきましょう。

まとめ

まとめです。

  • string.Formatは文字列生成に便利だが問題点が多い
  • 文字列補間による記法は以下の点で
    string.Formatの上位互換
    • 記述しやすい
    • 処理内容を理解しやすい
    • 間違いに早めに気づける
  • 既存コードも文字列補間に置き換えていくことを
    おススメしています

最後までお読みいただきありがとうございました。

コメント

タイトルとURLをコピーしました