男性か女性か、走っているか止まっているか歩いているかなど、何か固定値をグループにしておきたいことはよくある。
賢明なる読者諸兄は、そんなときに列挙型(enum型)を使うだろう。
1 2 3 4 5 6 |
enum AwesomeBeer { HoegaardenWhite = 0, Inedit = 1, ChimayBlue = 2 } |
例えば、こういう列挙型があったとして、それぞれのメンバーにint型の数値が割り当てられているとする。
今回は、数値をこの列挙型のメンバーへ変換する方法を探ってみたい。
一番簡単な方法には罠がある
まずは、列挙型で直接キャストする方法。
1 2 3 |
Console.WriteLine("0 is " + (AwesomeBeer)0); Console.WriteLine("1 is " + (AwesomeBeer)1); Console.WriteLine("2 is " + (AwesomeBeer)2); |
このコードは、問題なく列挙型のメンバーを返してくれて、期待どおり出力できる。
1 2 3 |
0 is HoegaardenWhite 1 is Inedit 2 is ChimayBlue |
これでいーじゃんとなれば、この記事はこれで終わり。そうは問屋がおろさない。ちょいと付き合っておくれやす。
さて、この方法は、定義されていない値を変換するときに困ってしまう。
1 |
Console.WriteLine("3 is " + (AwesomeBeer)3); |
このコードを実行するとちゃんと出力できる。
1 |
3 is 3 |
例外でも飛んでくれればいいのにと思うけれど、世はこともなし。
これじゃあ、変換に成功したかどうか判断できないね。
二人の真打ち
数値を列挙型に変換できるかどうかを知るにはどうすればいいだろう?
これには、Enum.IsDefined()という方法と、Type.IsEnumDefined()という2つの方法がある。前者は元々あったもので、後者は.Net 4.0から用意されている。
1 2 3 4 5 6 7 |
const int InvalidValue = 100; // For .Net 4.0 ~. typeof(AwesomeBeer).IsEnumDefined(InvalidValue); // For ~ .Net 3.5 Enum.IsDefined(typeof(AwesomeBeer), InvalidValue); |
どちらを使うべきか?
Enum.IsDefined()はリフレクションばりばりだから速度的に問題があるという記事を見つけたり、より最近用意された方法がいいのかなとか、雰囲気的にはType.IsEnumDefined()が良さそう。ということで、これをチェックするコードを書いてみた。
チェックコードでは、それぞれ100万回チェックさせてみて、その処理時間を計測した。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// 大チェック! var stopwatch = new Stopwatch(); var counter = 0; // For .Net 4.0 ~ [IsEnumDefined] stopwatch.Start(); while (counter < 1000000) { typeof(AwesomeBeer).IsEnumDefined(counter++); } stopwatch.Stop(); Console.WriteLine(string.Format( "IsEnumDefined takes {0} milliseconds", stopwatch.ElapsedMilliseconds)); stopwatch.Reset(); counter = 0; // For ~ .Net 3.5 [IsDefined] stopwatch.Start(); while (counter < 1000000) { Enum.IsDefined(typeof(AwesomeBeer), counter++); } stopwatch.Stop(); Console.WriteLine(string.Format( "IsDefined takes {0} milliseconds", stopwatch.ElapsedMilliseconds)); |
これを五回実行して、各処理時間の平均を取ると、
- ~ .Net 3.5 [IsDefined] : 686 ミリ秒
- .Net 4.0 ~ [IsEnumDefined] : 698 ミリ秒
微妙にIsDefinedが速かった。ネットの雰囲気に反して、IsDefinedを使うべきなのだろうか?
あるいは、特別な状況ではIsEnumDefinedが良いのだろうか?
いずれにせよ、100万回でこのぐらいの差なら、単純に数値を列挙型に変換する場合はどちらの方法でも変わりなさそうだ。
C#おすすめ図書
他の言語をやったことがあって、その言語の作法でC#を書いちゃったことのある人へ
他人にとって読みやすいコードを書くための永遠の名著