Keys

There are actually two StateMachine classes in this system:

Keyless StateMachine<TState> Keyed StateMachine<TKey, TState>
Contains only a single reference to the CurrentState and requires you to provide a reference to any other state you try to enter. Inherits from StateMachine<TState> and adds a Dictionary which allows you to register states with their own keys so that you can later enter those states by providing their key without needing an actual reference to the state itself.
Easy to create, extend, and debug. When you create a new state you only need to write the code that tries to enter it rather than also needing to make a key for it and modifying the initialization code to initialize it with all the other states. Requires a bit more effort to use, but is useful for abstraction and situations where you need to serialize the current state. For example, if you use an enum as the key you could save it to a file or send it over a network so that the other end can simply look up the state registered with that key.
public class Character : MonoBehaviour
{
  [SerializeField]
  private State _Idle;

  // Need public access to the state.
  public State Idle => _Idle;

  public StateMachine<State>
    FSM { get; private set; }

  protected virtual void Awake()
  {
    // Initialize with the starting state.
    // It doesn't know about any other
    // states until they are used.
    FSM = new StateMachine<State>(_Idle);
  }
}

// Empty
// space
// to
// line
// up
// with
// the
// other
// example
// code.

public class SomethingElse
{
  public void EnterIdle(Character character)
  {
    // We need a direct reference to the
    // FSM and the state to enter it:
    character.FSM
      .TryEnterState(character.Idle);
  }
}



public class Character : MonoBehaviour
{
  [SerializeField]
  private State _Idle;

  // No accessor needed with these keys.
  public enum Key { Idle, Walk }

  public StateMachine<Key, State>
    FSM { get; private set; }

  protected virtual void Awake()
  {
    // Initialize the StateMachine.
    FSM = new StateMachine<Key, State>();

    // Register the states with their key.
    // More can be registered later,
    // but that often defeats the purpose.
    FSM.Add(Key.Idle, _Idle);

    // Enter the starting state.
    FSM.ForceSetState(Key.Idle, _Idle);

    // Since there's only one state here we
    // could have just passed it into the
    // constructor, but the point is to show
    // the initialization.
  }
}

public class SomethingElse
{
  public void EnterIdle(Character character)
  {
    // We only need access to the FSM
    // since we can use the enum key:
    character.FSM
      .TryEnterState(Key.Idle);

    // And we still have the ability to use
    // direct references if necessary.
  }
}

The StateMachine<TKey, TState> class also has its own Input Buffer class and KeyChange<TKey> system for Changing States.