1

I'm trying to create a base class that works like a state machine and that can accept any type of enum:

    public class BaseFSM <T> where T : struct, IConvertible
{

    //Basic class that denote the transition between one state and another
    public class StateTransition
    {
        public  T currentState { get; set; }
        public  T nextState { get; set; }           

        //StateTransition Constructor
        public StateTransition(T currentState, T nextState)
        {
            this.currentState = currentState;
            this.nextState = nextState;
        }

        public override int GetHashCode()
        {
            return 17 + 31 * this.currentState.GetHashCode() + 31 * this.nextState.GetHashCode();;
        }

        public override bool Equals(object obj)
        {
            StateTransition other = obj as StateTransition;
            return other != null && this.currentState as Enum == other.currentState as Enum && this.nextState as Enum == other.nextState as Enum;
        }
    }

    protected Dictionary<StateTransition, T> transitions; //All the transitions inside the FSM
    public T currentState;
    public T previusState;

    protected BaseFSM() {
        // Throw Exception on static initialization if the given type isn't an enum.
        if(!typeof (T).IsEnum) 
            throw new Exception(typeof(T).FullName + " is not an enum type.");
    }

    private T GetNext(T next)
    {
        StateTransition transition = new StateTransition(currentState, next);
        T nextState;
        if (!transitions.TryGetValue(transition, out nextState))
            throw new Exception("Invalid transition: " + currentState + " -> " + next);
        return nextState;


    }
}

As you can see I defined both GetHashCode() and Equals(object obj). This is my implementation of my child class:

public class FSMPlayer : BaseFSM<PlayerState>
{   
    public FSMPlayer() : base()
    {           
        this.currentState = PlayerState.Idle;
        this.transitions = new Dictionary<StateTransition, PlayerState>
        {
            { new StateTransition(PlayerState.Idle, PlayerState.Run), PlayerState.Run }, //0
            { new StateTransition(PlayerState.Run, PlayerState.Jump), PlayerState.Jump }, //1
        };  
    }
}

As you can see in my child class I'm using my PlayerState Enum to define the state transitions. The problem it's when I try to use the getNext function because the TryGetValue always return false. The GetHashCode functions seams to work very well so I can't understand where the problem is. Thanks.

Heisenbug
  • 38,762
  • 28
  • 132
  • 190
Max_Power89
  • 1,710
  • 1
  • 21
  • 38

2 Answers2

3

The problem is here:

this.currentState as Enum == other.currentState as Enum

Enum is a reference type, so your enum gets boxed into a (new, unique) object. As a result it no longer compares equal to any other boxed instance.

enum types do the Right Thing for overriding Equals, though (as @hvd so correctly points out), so you can just do

this.currentState.Equals(other.currentState)
Jeroen Mostert
  • 27,176
  • 2
  • 52
  • 85
  • 2
    Wouldn't a simple `this.currentState.Equals(other.currentState)` work? –  Feb 22 '15 at 14:28
  • @hvd: yes, absolutely, I'm being silly. (Also, enums don't implement `IComparable`, but they do implement `IComparable`.) If you want to post this as an answer yourself, I'll delete mine. – Jeroen Mostert Feb 22 '15 at 14:32
  • You did identify the real problem, and you did post a valid way of solving it. I'm happy to just keep your answer. :) –  Feb 22 '15 at 14:34
0

You may also want to read the answers to this question to see why calling getHashCode on an enum is not recommended.

Using GetHashCode for getting Enum int value

Community
  • 1
  • 1
Wayne Tanner
  • 1,346
  • 11
  • 18
  • This is perfectly fine. The answer in that question discusses why you should not rely on `GetHashCode()` to return the numerical value of the enum. If you are actually using it to hash it (and so not relying on that property at all), there's no problem. – Jeroen Mostert Feb 22 '15 at 14:14