何を言い出すのか
enum、便利ですよね。入力値の制限ができますし、入力補完もききます。が、一つ問題があります。オリジナルを弄らずに値を追加することができないことです。
例えば、
ゲームか何かを作っていたとしてキャラクターの一つとして、現場猫を出すとしましょう。現場猫もいろんな種類がいます。定番の「ヨシ!」というやつ、受話器を持って「どうして」と悲嘆な表情なやつ、などなど。
我々の鬱憤や不満などそういった機微を混ぜこぜにして新しい現場猫を作りたくなるかも知れません。仮に適当なことを言うSEにでもしましょうか。ちょっと突っ込んだ質問をすると「まぁそれで良いんじゃないかと思います」とクライアントにロクに確認も取らずに生返事をして後々トラブルになりそうなアレです。警戒心ヤベェです。
その時、現場猫の種類をenumで管理してたとして、新しい現場猫を追加するとなれば、enumの宣言をどうすれば良いでしょうか。
publicenumGenbaCatType{NoProblem,//ヨシ!Why//どうして}publicenumExtendedGenbaCatType:GenbaCatType{ItsOkIThink//まぁそれで良いんじゃないかと思います}
少なくとも今(2019/11)はこんなことは出来ないです。
じゃぁclassにしてconstメンバだね
そうだね。
publicclassGenbaCatType{publicconstintNoProblem=0;//ヨシ!publicconstintWhy=1;//どうして}publicclassExtendedGenbaCatType:GenbaCatType{publicconstintItsOkIThink=2;//まぁそれで良いんじゃないかと思います}
まぁできなくはないのですが、いざ使うとなるとintで宣言しなければならず、何を表すか不明瞭です。加えて、intなので数字をフリーダムに入れることができます。できるなら避けたいですね。
じゃぁ、どないするのか
こんな感じで書いてみました。
usingSystem;classProgram{publicclassGenbaCatType{publicstaticreadonlyGenbaCatTypeNoProblem=newGenbaCatType(0);//ヨシ!publicstaticreadonlyGenbaCatTypeWhy=newGenbaCatType(1);//どうして?privatereadonlyint_type;privateGenbaCatType(){}protectedGenbaCatType(inttype){_type=type;}privateboolEquals(GenbaCatTypeother){returnGetHashCode()==other.GetHashCode();}publicoverrideboolEquals(objectobj){if(ReferenceEquals(null,obj))returnfalse;if(ReferenceEquals(this,obj))returntrue;if(obj.GetType()!=GetType())returnfalse;returnEquals((GenbaCatType)obj);}publicoverrideintGetHashCode(){return_type;}publicstaticbooloperator==(GenbaCatTypecat1,GenbaCatTypecat2){if(ReferenceEquals(cat1,null)&&ReferenceEquals(cat2,null))returntrue;if(ReferenceEquals(cat1,null))returnfalse;if(ReferenceEquals(cat2,null))returnfalse;returncat1.GetHashCode()==cat2.GetHashCode();}publicstaticbooloperator!=(GenbaCatTypecat1,GenbaCatTypecat2)=>!(cat1==cat2);publicstaticGenbaCatTypeCreate(inthashCode)=>newGenbaCatType(hashCode);}publicclassExtendedGenbaCatType:GenbaCatType{publicstaticreadonlyGenbaCatTypeItsOkIThink=newExtendedGenbaCatType(2);//それで大丈夫だと思います。privateExtendedGenbaCatType(inttype):base(type){}}staticvoidMain(string[]args){GenbaCatTypecatNoProblem=ExtendedGenbaCatType.NoProblem;GenbaCatTypecatOk=ExtendedGenbaCatType.ItsOkIThink;//GenbaCatType dog = new GenbaCatType(); //できませんGenbaCatTypemonkey;//これはできちゃう(中身はnull)Console.WriteLine(catNoProblem==catOk);//FalseConsole.WriteLine(catNoProblem==GenbaCatType.NoProblem);//True}}
上述のconstメンバからもう少し話を進めて、intからそのクラス型に制限、想定していないtypeの実体を作れないようコンストラクタをprotectedで隠蔽、比較演算子も実装して、表面上はenumのように使えるようにしました。入力補完もきいてくれるので打ち込むときも楽です。セーブ・ロードなどのシリアライズが必要になった時に備えてHashでの入出力も備えました。
難点としてはnullを持てちゃうこと、そしてenumに比べて宣言がクッソ面倒臭いことです。