ずっと勘ちがいしていたstring.Format()の挙動というか仕様

先日、ちょっとしたプログラムを作っていたところ、いままで(少なくとも5年くらい)ずっと勘ちがいしていたことに気付いたことがあったのでメモ代わりに残しておきます。


C#にはstringクラスという文字列を扱うためのプリミティブな型があります*1
このクラスには文字列を操作するための便利なメソッドがたくさん実装されています。その中でもわたしがすごく好きでよく使っているのがFormatメソッドです。これはどういうメソッドかというとCでいうところのprint系の関数と同じでして、文字列を整形して出力するためのメソッドです。


どんな感じで使うのかと言うとこんな感じで使います。

var x = 123;
Console.WriteLine(string.Format("(1) {0}\r\n(2) {0:00000}\r\n(3){0:D5}", x));

// ↓出力例
// 
// (1) 123
// (2) 00123
// (3) 00123

*2


第一引数には文字列を入れるのですが、その中に{n}という部分を入れておくと第二引数以降で渡した値がここに埋め込まれます。
第二引数が0番目になるので{0}、第三引数は1番目なので{1}という感じです。


また、上の例では{0:00000}とか{0:D5}とありますがこれはどちらも同じ意味で「先頭をゼロ詰めにして5桁で表示する」という指示です。


こんな感じですごく簡単なのに便利なので重宝していたのですが、実はstring.Formatの第二引数以降に渡す値が数値ではなく文字列の場合は先頭ゼロ詰めという指示が有効ではないらしいのです。


どういうことかというとこんな感じになります。

var x = 123;
Console.WriteLine(string.Format("(1) {0}\r\n(2) {0:00000}\r\n(3){0:D5}", x.ToString()));

// ↓出力例
// 
// (1) 123
// (2) 123
// (3) 123


最初の例ではintが第二引数に渡っていたのですが、この例ではそれをstringに変換してから渡しています。すると指定した書式はすべて無視されてそのまま表示されます。


もともとD5というのはDecimalのDだろうから数値前提だと言われたら返す言葉がないのですが、でもなあ...。


え?当たり前??

*1:厳密にはstringはSystem.Stringのエイリアスですがここではとりあえず置いておきます

*2:Console.WriteLine()自体がstring.Formatと同じような形式で整形する文字列を受け取れるのですが、ここでは分かりやすくするためにあえてstring.Formatを入れています。