デリゲートで組まれたStateMachine

# デリゲートパターンなStateMachine
コガネブログさんのStateMachineを一部付け加えた物です。

## 付け加えた所
### 現時点のStateのKeyを取得出来るようにした。
* たまに外部からStateMachineの状態を知りたくなる時があった。

### ジェネリックに型制約を付けた。
* 過去のプロジェクトでStateMachineのキーにEnumしか使った事がなかったので、型制約を付けた。
    * Enum以外をキーとして使うStateMachineを使うパターンってあるのかな・・？

### Stateの切り替え時に発生するGCを無くした。
* EnumをキーとするDictionaryにアクセスすると、GCが発生するのでそれを無くすようにした。

### Stateの切替時にコールバックを発火出来るようにした。
* たまにステートの切替時に、専用の処理が必要だった場合があった。


## コード
```csharp:StateMachine.cs
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

/// <summary>
/// ステートマシン
/// ジェネリックのパラメーターはEnumを指定して下さい
/// </summary>
public class StateMachine<T> : IEqualityComparer<T> where T : struct
{
	/// <summary>
	/// ステート
	/// </summary>
	private class State
	{
		private readonly Action mEnterAct;  // 開始時に呼び出されるデリゲート
		private readonly Action mUpdateAct; // 更新時に呼び出されるデリゲート
		private readonly Action mExitAct;   // 終了時に呼び出されるデリゲート

		/// <summary>
		/// コンストラクタ
		/// </summary>
		public State(Action enterAct = null, Action updateAct = null, Action exitAct = null)
		{
			mEnterAct = enterAct ?? delegate { };
			mUpdateAct = updateAct ?? delegate { };
			mExitAct = exitAct ?? delegate { };
		}

		/// <summary>
		/// 開始します
		/// </summary>
		public void Enter()
		{
			mEnterAct();
		}

		/// <summary>
		/// 更新します
		/// </summary>
		public void Update()
		{
			mUpdateAct();
		}

		/// <summary>
		/// 終了します
		/// </summary>
		public void Exit()
		{
			mExitAct();
		}
	}

	private Dictionary<T, State> mStateTable;  // ステートのテーブル
	private Dictionary<T, Dictionary<T, Action>> mSwitchTable;
	private State mCurrentState;                              // 現在のステート
	public T currentState { get; private set; }

	public StateMachine()
	{
		System.Type type = typeof(T);
		UnityEngine.Debug.AssertFormat(type.IsEnum, "ステートのKeyにEnumではない型が指定されています");

		mStateTable = new Dictionary<T, State>(this);
		mSwitchTable = new Dictionary<T, Dictionary<T, Action>>(this);
	}

	/// <summary>
	/// ステートを追加します
	/// </summary>
	public void Add(T key, Action enterAct = null, Action updateAct = null, Action exitAct = null)
	{
		mStateTable.Add(key, new State(enterAct, updateAct, exitAct));
	}

	public void AddSwitchCallBack(T prevState, T nextState, Action switchAct)
	{
		// すでにprevStateとnextStateの組み合わせでコールバックが仕込まれていたら
		// prevStateとnextStateの組み合わせのコールバックは１つのみ
		if (mSwitchTable.ContainsKey(prevState) && mSwitchTable[prevState].ContainsKey(nextState))
		{
			UnityEngine.Debug.LogError(prevState.ToString() + " - " + nextState.ToString() + "には既にSwitchコールバックが登録されています。");
			return;
		}

		if (!mSwitchTable.ContainsKey(prevState))
		{
			mSwitchTable.Add(prevState, new Dictionary<T, Action>(this));
		}
		mSwitchTable[prevState].Add(nextState, switchAct);
	}

	/// <summary>
	/// 現在のステートを設定します
	/// </summary>
	public void SetState(T key)
	{
		if (mCurrentState != null)
		{
			mCurrentState.Exit();
		}

		if (mSwitchTable.ContainsKey(currentState) && mSwitchTable[currentState].ContainsKey(key))
		{
			mSwitchTable[currentState][key]();
		}

		mCurrentState = mStateTable[key];
		currentState = key;
		mCurrentState.Enter();
	}

	/// <summary>
	/// 現在のステートを更新します
	/// </summary>
	public void Update()
	{
		if (mCurrentState == null)
		{
			return;
		}
		mCurrentState.Update();
	}

	/// <summary>
	/// すべてのステートを削除します
	/// </summary>
	public void Clear()
	{
		mStateTable.Clear();
		mCurrentState = null;
	}

	public bool Equals(T x, T y)
	{
		return CastTo<int>.From(x) == CastTo<int>.From(y);
	}

	public int GetHashCode(T obj)
	{
		return CastTo<int>.From(obj);
	}
}

public static class CastTo<T>
{
	private static class Cache<S>
	{
		static Cache()
		{
			// 次のラムダ式を式木で構築
			// (S s) => (T)s
			var p = Expression.Parameter(typeof(S), "target");
			var c = Expression.ConvertChecked(p, typeof(T));
			Caster = Expression.Lambda<Func<S, T>>(c, p).Compile();
		}
		internal static readonly Func<S, T> Caster;
	}

	public static T From<S>(S source)
	{
		return Cache<S>.Caster(source);
	}
}
```

## 引用元
[コンパイラをごまかすキャスト](http://csharpvbcomparer.blogspot.com/2014/09/net-cast-cheats-compiler.html)
[【C#】デリゲートを使用したステートパターン](http://baba-s.hatenablog.com/entry/2017/12/28/205800)