忍ばないノート

UnityやC#をメインに、開発記やTipsを書いています

【C#】enumのToStringやParse等の操作を低コストで楽にするクラス

はじめに

今回はenumの操作を低コストに抑え、且つ楽にするクラス紹介したいと思います。

よくあるもの

enumの操作に関して、特にToString()が遅いだのなんだのという話がたまに聞こえてきます。
実際、特に対策もしないで書いたコードだとboxingが発生したり、リフレクションが走ってしまいます。
それに対する対策として、定義したEnumに対するToString拡張メソッドを定義、自動生成する方法とかよくありますね。
こんなのとか

enum ECurrencyType
{
    None,
    JPY,
    USD,
    EUR,
    GBP,
    CHF,
}

public static string ToString ( this ECurrencyType self )
{
    switch ( self )
    {
        case ECurrencyType.None: return "None";
        case ECurrencyType.JPY:  return "JPY";
        case ECurrencyType.USD:  return "USD";
        case ECurrencyType.EUR:  return "EUR";
        case ECurrencyType.GBP:  return "GBP";
        case ECurrencyType.CHF:  return "CHF";
    }

    return "";
}

めんどくさい

enumの型の種類が少ないなら別に良いと思うのですが、
ゲームとか作っていると大量に定義したりされていたりしていて、
いちいち定義するのも、自動生成してコンパイル時間を待つのもめんどうです。
更に、定義済みのenumに手を加えるものなら。。。(白目
あと個人的に、enum操作するコードが冗長に感じられ、書くのがめんどくさかったりします(オイ

こんなのとか

enum ECurrencyType
{
    None,
    JPY,
    USD,
    EUR,
    GBP,
    CHF,
}

// enumを整数へ
var enum2Int = ( int ) ECurrencyType.JPY;

// 整数をenumへ
var int2Enum = ( ECurrencyType ) 2;

// 文字列をenumへ
Enum.TryParse<ECurrencyType>( "EUR", out var string2Enum );

// enumを文字列へ
var enum2String = ECurrencyType.GBP.ToString ();

楽する

っというわけで楽したいと思います。
まずはソースを。

EnumHelper.cs

(ここをクリック or タップで展開・格納可)

使い方

処理自体は隠蔽されている内部クラスが行うので、公開されているメソッドを呼ぶだけで一通りの操作ができるようになっています。
下記がEnumHelper使用のサンプルコードになります。

enum ECurrencyType
{
    None,
    JPY,
    USD,
    EUR,
    GBP,
    CHF,
}

// 32bit整数へ
int int32 = EnumHelper.ToInt32 ( ECurrencyType.JPY );

// 64bit整数へ
long int64 = EnumHelper.ToInt64 ( ECurrencyType.USD );

// 整数からenumへ
ECurrencyType euro = EnumHelper.ToValue <ECurrencyType> ( 3 );

// enumから文字列へ
string name = EnumHelper.GetName ( ECurrencyType.GBP );

// 文字列からenumへ
ECurrencyType currency = EnumHelper.TryParse <ECurrencyType> ( "CHF" ) ?? ECurrencyType.None;

※ Unityで使うときは、Project Settings > Player > Other Settings > Allow 'unsafe' code にチェックを入れて下さい。 setting on the Unity

終わりに

内部クラスの実装をみるとわかりますが、変換元のポインターを取ってきて、目的の型に変換しているのでBoxingが発生することなく、結構高速に動作します。
後はキャスト式や演算子が不要だったり、参照渡しもする必要がないので、シンプルなコードを書きやすくなるのではないかと思います。

参考

qiita.com

ufcpp.net

【C#】enumをビットフラグとしていい感じに使えるようにするクラス

はじめに

今回はenumをビットフラグとして利用し、設定や確認を楽にするクラスを紹介したいと思います。

ビットフラグ

ビットフラグとは、2進数の0, 1をフラグとして利用するものです。
ビット演算を行い、フラグを立てたり消したりしていきます。
詳しくは、こちらの記事が非常に参考になるかと思います。
(新人時代、大変お世話になりました...!)

yas-hummingbird.blogspot.com

めんどくさい

ビットフラグ、単純にメモリの節約だったりキャラクターの状態異常フラグ等としてよく使うのですが、 種類が多くなってくるとenumの様な名前付きの値で管理したくなります。実際楽です。
しかし、何度もビット演算を行ってフラグを立てたり消したり確認したりするのが個人的にめんどくさいし、同じようなコードを書きたくないと思うのです。
例えばこんな、、、

public class Player
{
    enum EState
    {
        None      = 0,
        Poisoned  = 1,          // 毒
        Confusion = ( 1 << 1 ), // 混乱
        Darkness  = ( 1 << 2 ), // 暗闇
    }

    EState flag;

    void Hoge ()
    {
        // 毒と暗闇になった
        flag = EState.Poisoned | EState.Darkness;

        // 諸々あって毒解除
        flag &= ~EState.Poisoned;

        // 本当に消えてる?
        bool hasPoison = ( flag & EState.Poisoned ) != 0;
        
        // 混乱追加
        flag |= EState.Confusion;

        // 暗闇解除
        flag &= ~EState.Darkness;
    }
}

楽する

じゃあクラス化して楽しよう(゚∀゚)
とりあえず、ソースを。
32bit用, 64bit用を用意してみました。

BitFlag

通常用
こんんなのとか

enum EState
{
    None = 0,
    Poisoned,  // 毒
    Confusion, // 混乱
    Darkness,  // 暗闇
}

BitFlag.cs

使い方
enum EState
{
    None = 0,
    Poisoned,  // 毒
    Confusion, // 混乱
    Darkness,  // 暗闇
}

var flag = new BitFlag<EState> ();

// 毒と暗闇になった
flag.SetFlag ( EState.Poisoned );
flag.SetFlag ( EState.Darkness );

// 諸々あって毒解除
flag.RemoveFlag ( EState.Poisoned );

// 本当に消えてる?
bool hasPoison = flag.HasFlag ( EState.Poisoned );

// 混乱追加
flag.SetFlag ( EState.Confusion );

// 暗闇解除
flag.RemoveFlag ( EState.Darkness );

BitEnumFlag

2の累乗値で定義しているもの向け。
(良いクラス名が思いつかなかった。。。後で更新するかもです)
こんなのとか

enum EState
{
    None      = 0,
    Poisoned  = 1,          // 毒
    Confusion = ( 1 << 1 ), // 混乱
    Darkness  = ( 1 << 2 ), // 暗闇
}

BitEnumFlag.cs

使い方
enum EState
{
    None      = 0,
    Poisoned  = 1,          // 毒
    Confusion = ( 1 << 1 ), // 混乱
    Darkness  = ( 1 << 2 ), // 暗闇
}

var flag = new BitEnumFlag<EState> ();

// 毒と暗闇になった
flag.SetFlag ( EState.Poisoned | EState.Darkness );

// 諸々あって毒解除
flag.RemoveFlag ( EState.Poisoned );

// 本当に消えてる?
bool hasPoison = flag.HasFlag ( EState.Poisoned );

// 混乱追加
flag.SetFlag ( EState.Confusion );

おわりに

普段はBitEnumFlagを使いつつ、明示的に値を割り当てていないenumに対してはBitFlagを使うと良いかなと思います。
あと、各クラスのジェネリックメソッド内ではBoxingが発生します。
パフォーマンスを気にするのであれば、int や long にキャストしてからSetFlag() に投げると良いかなと思います。

参考

ufcpp.net

Unityでよく使う便利拡張メソッド

はじめに

忍くんです。
手始めに、Unityで非常によく使う拡張メソッドを簡単に載せてみます。
便利コードやツール、テーマを決めての実装、開発日記やその他技術系などのことを書いていこうと思っています。
どうぞよろしゅうー (=゚ω゚)ノ

SetActive操作


Component操作


使用例


参考

拡張メソッドとは

ufcpp.net