列挙型の個々の要素に割り当てられる数値は、すでに述べたように、何も指定しなければ0から順番に1ずつ大きくなる値が割り当てられる。しかし、これは明示的に指定することで変更することができる。List 16-8はそれを行った例である。
1: using System;
2:
3: namespace Sample008
4: {
5: enum Sample
6: {
7: A,
8: B,
9: C = 10,
10: D,
11: E = B
12: }
13: class Class1
14: {
15: [STAThread]
16: static void Main(string[] args)
17: {
18: Console.WriteLine( (int)Sample.A );
19: Console.WriteLine( (int)Sample.B );
20: Console.WriteLine( (int)Sample.C );
21: Console.WriteLine( (int)Sample.D );
22: Console.WriteLine( (int)Sample.E );
23: }
24: }
25: }
これを実行するとFig.16-8のようになる。
実行結果を見て分かるとおり、何も指定していない7〜8行目のAとBは0と1になる。しかし、9行目のCは、明示的にイコール10としているので、10という値を持つ。次の10行目のDは、Cの次なので、10の次、つまり11になる。最後の11行目のEは、イコールBとして、Bと同じとしているため、Bの値つまり1になる。
列挙型の値を変更するのは、数値に何か特別な意味があって、それと一致させたい場合である。逆に数値に特別な意味がないのに、数値を明示的に指定すると予期せぬトラブルが起こる場合があるので、必要性を見極めてから利用しよう。
List 16-9は、数値指定時に起こるトラブルの一例である。内容としては、ただ単に列挙型の値をコンソールに出力しているだけで何も問題がないように見える。
1: using System;
2:
3: namespace Sample009
4: {
5: enum Sample
6: {
7: A,
8: B,
9: C = 10,
10: D,
11: E = B
12: }
13: class Class1
14: {
15: [STAThread]
16: static void Main(string[] args)
17: {
18: Console.WriteLine( Sample.A );
19: Console.WriteLine( Sample.B );
20: Console.WriteLine( Sample.C );
21: Console.WriteLine( Sample.D );
22: Console.WriteLine( Sample.E );
23: }
24: }
25: }
これを実行するとFig.16-9のようになる。
見て分かるとおり、本来A〜Eが出力されるはずが、Bが出るべき個所がEに化けている。その原因は、11行目の「E = B」にある。列挙型が内部的に数値で処理されるため、同じ数値に割り当てられた名前は区別することができない。つまり、Sample.Bを使ったのかSample.Eを使ったのかは、列挙型の値からは区別することができない。そこで、Sample.Bを指定しているのに、同じ値を持つEがコンソールに出てしまうという現象が起きるのである。これを回避するには、同じ値を別の名前に割り当てなければよい。数値を明示的に指定しなければ自動的に同じ値は回避されるので、初心者は、数値指定は避けた方がトラブルに巻き込まれる確率が低くなる。
列挙型は、数値ではないが、内部的に数値として処理される。そのため、数値計算のうち、限定的な範囲が実行できるようになっている。List 16-10は列挙型を数値計算に用いたサンプル・ソースである。
1: using System;
2:
3: namespace Sample010
4: {
5: enum Sample
6: {
7: A,
8: B,
9: C
10: }
11: class Class1
12: {
13: [STAThread]
14: static void Main(string[] args)
15: {
16: Sample a = Sample.A;
17: Sample r = a + 1;
18: Sample b = Sample.B;
19: // Sample s = a + b; // 演算子 '+' を 'Sample010.Sample' と 'Sample010.Sample' 型のオペランドに適用することはできません。
20: Console.WriteLine( r );
21: }
22: }
23: }
これを実行するとFig.16-10のようになる。
17行目は、Aが代入されている変数aに1を足すコードが記述されている。このコードは正常に実行でき、結果はBとなる。これは、5〜10行目の列挙型の定義で、Aの次がBであることによる結果である。一方、19行目のように、列挙型の値と列挙型の値を足し算するような計算はエラー扱いとなり、コンパイルできない。Aに1を足すと次の要素を示す、という計算に意味はあっても、AとBを足す、という計算に意味はないからだ。
C#の列挙型は、すべて、System.Enumクラスを継承したものとして自動的に定義される。そのため、System.Enumクラスを処理するためのさまざまなメソッドがすべて利用できる。例えば、列挙型のある値に対応する名前を得るには、List 16-11のように記述することができる。
1: using System;
2:
3: namespace Sample011
4: {
5: enum Era
6: {
7: Meiji,
8: Taisho,
9: Showa,
10: Heisei
11: }
12: class Class1
13: {
14: [STAThread]
15: static void Main(string[] args)
16: {
17: Era t = Era.Taisho;
18: string name = Era.GetName( Type.GetType("Sample011.Era"), t );
19: Console.WriteLine( name );
20: }
21: }
22: }
これを実行するとFig.16-11のようになる。
ポイントは18行目である。GetNameメソッドは、System.Enumクラスのstaticなメソッドである。第1引数に列挙型の型を、第2引数に判定したい値を渡すと、列挙型の名前を返してくれる。つまり、Era.Taishoは内部的に数値に対応づけられているが、その対応を逆にたどって数値から対応する名前を調べて、「Taisho」という文字列が返される。
Copyright© Digital Advantage Corp. All Rights Reserved.