Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9374

C#でJavaのenumを再現する

$
0
0

この記事は StudioZ Tech Advent Calendar 2019の11日目の記事です。

はじめに

5年ぐらい前からUnityを使うようになり、JavaからC#に転向しました。
似たところの多い言語なのでほとんど違和感なく使うことができたのですが、唯一困ったのがenumの仕様。
そこでJavaのenumをC#で再現できないか考えてみました。

Javaのenumの特性と実装方法

  • 列挙子は列挙型のインスタンスを持つ
    • 普通にクラスを使えば満たせます
  • 外部からインスタンスの生成ができない
    • コンストラクタをprivateにすることで対応可能です
  • 列挙型.列挙子でアクセスできる
    • 列挙子のインスタンスをstaticフィールドで管理すればアクセス可能になります
  • 同名の列挙子は定義できない
    • 同名のフィールドは定義できないので、staticフィールドで管理する時点で満たされます

ここまでの条件をコードにするとこんな感じになります

publicclassEnumSample{publicstaticEnumSampleEnum1=newEnumSample();publicstaticEnumSampleEnum2=newEnumSample();publicstaticEnumSampleEnum3=newEnumSample();publicstaticEnumSampleEnum4=newEnumSample();// 外部からのインスタンス生成不可privateEnumSample(){}}
  • 列挙子は固有の値を持つことができる
    • コンストラクタで値を受け取ってフィールドに持つようにします
    • ただし静的に重複のチェックをすることはできません(実行時のチェックは可能)
publicclassEnumSample2{publicstaticEnumSample2Enum1=newEnumSample2(1);publicstaticEnumSample2Enum2=newEnumSample2(2);publicstaticEnumSample2Enum3=newEnumSample2(3);publicstaticEnumSample2Enum4=newEnumSample2(4);publicintNum;// 外部からのインスタンス生成不可privateEnumSample2(intnum){Num=num;// 重複チェックを入れるとしたらこの辺でやる// Dictionaryに詰めていってContainsKeyでチェックするのが楽そう}}
  • インスタンスごとにメソッドを実装できる
    • 列挙体クラスを抽象クラスにし、列挙子ごとにクラスを継承してメソッドを実装します
    • 列挙子ごとに実装必須にしたい場合はabstract、デフォルト動作を定義したい場合はvirtualにします
    • 列挙子間で共通化したい処理は親クラス側、差別化したい処理は子クラス側に実装することで重複処理をなくすことができます
  • 列挙体はインタフェースを実装できる
    • 列挙型にインタフェースを実装する宣言をするだけです
// ドロップアイテムのインタフェースpublicinterfaceIDropItem{stringName{get;}intPrice{get;}stringDescription{get;}}// ドロップアイテムとして扱える武器の列挙型publicabstractclassWeapon:IDropItem{publicstaticWeaponSword=newWeaponSword();privateclassWeaponSword:Weapon{// コンストラクタがpublicでもクラス自体はprivateなので外部からはインスタンス生成不可publicWeaponSword(){}publicoverridestringName{get{return"剣";}}publicoverrideintPrice{get{return100;}}publicoverridestringDescription{get{return"いい感じの説明";}}publicoverridevoidAttack(){base.Slash();}}publicstaticWeaponSpear=newWeaponSpear();privateclassWeaponSpear:Weapon{publicWeaponSpear(){}publicoverridestringName{get{return"槍";}}publicoverrideintPrice{get{return50;}}publicoverridestringDescription{get{return"そんな感じの説明";}}publicoverridevoidAttack(){base.Pierce();}}publicstaticWeaponClub=newWeaponClub();privateclassWeaponClub:Weapon{publicWeaponClub(){}publicoverridestringName{get{return"棍棒";}}publicoverrideintPrice{get{return10;}}publicoverridestringDescription{get{return"とてもアレな説明";}}// Attack()の実装は省略可能}protectedWeapon(){}// 名前、価格、説明は必ず列挙子で定義するpublicabstractstringName{get;}publicabstractintPrice{get;}publicabstractstringDescription{get;}// 攻撃処理の実装は省略可能publicvirtualvoidAttack(){// オーバーライドしない場合はデフォルトとして殴る処理Strike();}// protectedで宣言したプロパティやメソッドは列挙子側からアクセスできるprotectedvoidSlash(){// 切る武器の処理}protectedvoidPierce(){// 刺す武器の処理}protectedvoidStrike(){// 殴る武器の処理}}

C#ではできないこと

  • 列挙子をswitchの条件に使う
    • caseには定数しか使えないので、実態がクラスである列挙子を使うことはできません
    • とはいえ列挙子側に処理を実装できるので、switchは基本的に使う必要はないと思います

おわりに

ぱっと見面倒そうに見えますが、Java式enumを使うことによって、ポリモーフィズムを実現しやすくなります
機会があったらぜひ試してみてください

参考

Javaの定数はEnumで! Enumの使い方から考え方までお伝えします


Viewing all articles
Browse latest Browse all 9374

Latest Images

Trending Articles