xin9le.net

Microsoft の製品/技術が大好きな Microsoft MVP な管理人の技術ブログです。

暗黙的にenumに変換できるゼロ

ここ最近、#じんぐる先生とか言って部署の新人さんにC#プログラミングを教えています。クラス/継承/多態などのオブジェクト指向の基礎から時間を掛けてみっちりと。そんな中、例題を出して説明しながら答え合わせしていたとき次のような現象に出くわしました。

enum Fruits
{
    Apple = 0,
    Orange,
    Peach
}

static void Main()
{
    Fruits apple  = 0;          //--- エラーにならない!
    Fruits orange = 1;          //--- コンパイルエラー (int を Fruits に変換できない)
    Fruits peach  = (Fruits)2;  //--- 明示的な型変換によりエラーにならない
}

「int型は列挙型に明示的な変換しかできない」と思っていたので、0だけコンパイルエラーにならない事実を知って驚きました。型変換は(基本的に)明示的な場合も暗黙的な場合も型単位で挙動が決定されるものなので、同じ型の中でも特定の値だけ挙動が違うものがあることを知りませんでした。

  1. int型の1が列挙型に型変換できない
  2. 0はint型
  3. 0も列挙型に型変換できなくて当然

三段論法的にこんなイメージだったので「もしかしてバグなんじゃないだろうか...」と軽く思いつつ、いつまでも悶々と するのもアレだったので世界中のC#/VBのMVPさんたちにメールで質問してみました。そしたらビックリするほどすぐに返信があり、ひと言「By design (仕様です)」。

ドキュメントには確かに書いてある

メールを返してくれたMVPさんと@tanaka_733さんに教えて頂いたのですが、MSDNには確かに次のようなドキュメントがあります。

6.1.3 暗黙の列挙値変換 暗黙の列挙値変換を利用すると、decimal-integer-literal の 0 を任意の列挙型に変換できます。

全く知らなかったです。しかし0だけ特別な理由が未だにわからない。0だけ特別扱いしなければならない理由は何なんだろうか。メールを返してくださったMVPさんの付加情報によると以下が参考になるようです。英語は得意ではないので、日本語で説明してくださる優しい方を募集しております!

The Root of All Evil, Part One
The Root of All Evil, Part Two

知らない事は誰にでもありますよね

また、これと同じ内容を酢酸先生 (@ch3cooh) も早速書いています。この仕様を知っていた人ってどれくらいいるのかな。今回は後輩に教えているつもりが新たなことを教えられました。善きこと哉。

列挙型変数に0が代入できる

[追記 : 2013/12/14 14:33]
理由は「列挙型の既定値がゼロ」だから

Microsoftエバンジェリストの荒井さんからFacebookでコメントを頂きました。「列挙型は値型で既定値が0だからだ」と。先のメールで回答を下さったMVPさんも同じことを言っていたのですが、「そんな事は知ってるよ」と流していました。でもよくよく考えてみると、やっぱりそれが答えなんですね。

static void Main()
{
    Fruits fruits;                    //--- 省略したら既定値
    Fruits fruits = default(Fruits);  //--- つまりコレと同じ
    Fruits fruits = 0;                //--- 列挙型の既定値は常に0なのでコレとも同じ
}

既定値が0である以上、0は常に代入できなければならない。質問しておいてちゃんと考えずに流すなんて、失礼かつ浅はかの極みでした...orz