C# - Limiting a generic type to an Enum

Ok, I have googled around this but found conflicting information and hideous reflection being used.

Context:

I have a generic state machine:

public class StateMachine<T>
{
    protected Dictionary<T, StateHandler<T>> mHandlers;

    public StateHandler<T> Current;

    public StateMachine(StateHandler<T> _startState)
    {
        mHandlers = new Dictionary<T, StateHandler<T>>();
        mHandlers.Add(_startState.StateCode, _startState);
        _startState.Start(null);
        Current = _startState;
    }

    public void Add(StateHandler<T> _new)
    {
        mHandlers.Add(_new.StateCode, _new);
    }


    public void ChangeState(T _new)
    {
        if (mHandlers.Count > 0)
        {

            if (Current.CanChange(_new))
            {
                StateHandler<T> a;
                if (mHandlers.TryGetValue(_new, out a))
                {
                    Current.Stop(a);
                    a.Start(Current);
                    Current = a;
                }
                else
                {
                    throw new Exception("State machine does not contain a handler for  " + _new);
                }
            }
            else
            {
                throw new Exception("Invalid state change from " + Current.StateCode + " to " + _new);
            }
        }
    }
}

Which contain a collection of state handlers:

public abstract class StateHandler<T>
{
    public readonly T StateCode;
    public abstract void Start(StateHandler<T> _oldState);
    public abstract void Stop(StateHandler<T> _newState);
    protected HashSet<T> mAllowedChanges;

    public void AddAllowedChange(T _state)
    {
        mAllowedChanges.Add(_state);
    }

    public void RemoveAllowedChange(T _state)
    {
        mAllowedChanges.Remove(_state);
    }

    public bool CanChange(T _newState)
    {
        return mAllowedChanges.Contains(_newState);
    }

    public StateHandler(T _code)
    {
        mAllowedChanges = new HashSet<T>();
        StateCode = _code;
    }
}

This works really well. In my game I have a enum represeting possible states I can do this:

mGameStateManager = new StateMachine<MakiGameState>(a);
        
         InGameState s = new InGameState();
         s.Game = this;
         s.AddAllowedChange(MakiGameState.Paused);
         s.AddAllowedChange(MakiGameState.GameOver);
         mGameStateManager.Add(s);

However I can use anything to represent the state code but I would really like to limit T to be a type of enum. I know how to limit generics using where but there doesn’t seem to be a way to force T to be of type enum only.

Any C# gurus have any advice?

Thanks.

Ok after more googling seems like this should work but will generate an CS0702 error stating the Enums cant be used to constrain a generic type. Even though the C# spec says it should be possible. I suppose I could use struct to limit it value types?

From memory this is one of those really bizarre things that cannot be done in C#, (or at least the version of mono unity is using).

@lordofduct or @eisenpony may be able to confirm this.

There are weird workarounds that will give you runtime errors. But I don’t think the compiler will do it for you.

1 Like

Unfortunately can’t be done in C#/.Net.

It’s really annoying.

What I do is always constrain to ā€˜struct, IConvertible’. As you can see here in my ConvertUtil (one of many places I do this):

quick look:

  #region ToEnum

   public static T ToEnum<T>(string val, T defaultValue) where T : struct, System.IConvertible
   {
     if (!typeof(T).IsEnum) throw new System.ArgumentException("T must be an enumerated type");

     try
     {
       T result = (T)System.Enum.Parse(typeof(T), val, true);
       return result;
     }
     catch
     {
       return defaultValue;
     }
   }

   public static T ToEnum<T>(int val, T defaultValue) where T : struct, System.IConvertible
   {
     if (!typeof(T).IsEnum) throw new System.ArgumentException("T must be an enumerated type");

     object obj = val;
     if(System.Enum.IsDefined(typeof(T), obj))
     {
       return (T)obj;
     }
     else
     {
       return defaultValue;
     }
   }

   public static T ToEnum<T>(object val, T defaultValue) where T : struct, System.IConvertible
   {
     return ToEnum<T>(System.Convert.ToString(val), defaultValue);
   }

   public static T ToEnum<T>(string val) where T : struct, System.IConvertible
   {
     return ToEnum<T>(val, default(T));
   }

   public static T ToEnum<T>(int val) where T : struct, System.IConvertible
   {
     return ToEnum<T>(val, default(T));
   }

   public static T ToEnum<T>(object val) where T : struct, System.IConvertible
   {
     return ToEnum<T>(System.Convert.ToString(val), default(T));
   }

   public static System.Enum ToEnumOfType(System.Type enumType, object value)
   {
     return System.Enum.Parse(enumType, System.Convert.ToString(value), true) as System.Enum;
   }

   public static bool TryToEnum<T>(object val, out T result) where T : struct, System.IConvertible
   {
     if (!typeof(T).IsEnum) throw new System.ArgumentException("T must be an enumerated type");

     try
     {
       result = (T)System.Enum.Parse(typeof(T), System.Convert.ToString(val), true);
       return true;
     }
     catch
     {
       result = default(T);
       return false;
     }
   }

   #endregion

In the static constructor of your generic class you can perform a quick type test (like I do in my ToEnum method), and throw an exception:

public SomeClass<T> where T : struct, System.IConvertible
{

   static SomeClass()
   {
     if(!typeof(T).IsEnum) throw new System.ArgumentException("The generic class SomeClass<T> requires the type parameter to be an enum.");
   }

}

Because it’s during the static constructor, it’ll only test the first time you even touch the generated concrete class. Any subsequent instantiations won’t need to test as it’s already validated.

2 Likes

Thanks, I was hoping to avoid turning what should be a compile time constraint into a run-time error but that is better than nothing.

Cheers!

Just a question:

Conceptually speaking, what is different between your State and your StateHandler? Maybe your State should be a class and be its own StateHandler.

Ok, a StateHandler represents a single state that an entity (in this case the game itself) can be in. It has a few properties:

  • StateCode - the unique ID that represents this state. This was the generic type T that I wanted to limit to enums.

  • Two abstract methods - Start/Stop that allow the state to be Started and Stopped (build the state and tear down the state)

  • Some methods to provide a way of limiting state changes (the HashSet and the Add/Remove/CanChange methods)

The StateMachine class itself is a collection of StateHandlers and provides the logic for changing state. It enforces the valid state changes. Invokes the Start/Stop method and provides access to the current state.

Code example:

This is the pause state in my game:

public class PauseState : StateHandler<MakiGame.MakiGameState>
{
    public MakiGame Game;
    public PauseState()
        : base(MakiGame.MakiGameState.Paused)
    {

    }

    public override void Start(StateHandler<MakiGame.MakiGameState> _oldState)
    {
    
        Game.Pause(true);
    }

    public override void Stop(StateHandler<MakiGame.MakiGameState> _newState)
    {
        Game.Unpause();
    }
}

The GameManager class creates this state and adds it to its state machine at startup:

protected void CreateStateMachine()
     {

         IntroState intro = new IntroState();
         intro.Game = this;
         intro.AddAllowedChange(MakiGameState.InGame);
       
         mGameStateManager = new StateMachine<MakiGameState>(intro);
        
         PauseState paused = new PauseState();
         paused.Game = this;
         paused.AddAllowedChange(MakiGameState.InGame);
         mGameStateManager.Add(paused);
     }

Then at some stage in the game when the paused button is clicked:

 // the paused button will use this
    public void PauseButtonClick()
    {
        if (mStateMachine.Current.StateCode == MakiGameState.Paused)
        {
            mStateMachine.ChangeState(MakiGameState.InGame);
        }
        else
        {
            mStateMachine.ChangeState(MakiGameState.Paused);
        }
    }

The StateMachine finds the correct handler, notifies the current state to stop. Notifies the new state to start and then sets the new state as the Current.

But if the state change was invalid or there were no registered handlers for the requested state then it will throw up some an exception.

The StateMachines ChangeState method looks like this:

public void ChangeState(T _new)
    {
        if (mHandlers.Count > 0)
        {

            if (mCurrent.CanChange(_new))
            {
                StateHandler<T> a;
                if (mHandlers.TryGetValue(_new, out a))
                {
                    mCurrent.Stop(a);
                    a.Start(Current);
                    mCurrent = a;
                }
                else
                {
                    throw new Exception("State machine does not contain a handler for  " + _new);
                }
            }
            else
            {
                throw new Exception("Invalid state change from " + mCurrent.StateCode + " to " + _new);
            }
        }
    }

Hope that explains it well enough. Any advice about improving it is much appreciated. This it the first time I have rolled a generic state machine so I was shooting in the dark a bit.

I’d like to stop right there. To me, your StateCode describes a ā€œmagic numberā€. They are quite often used in conditionals but in your case, I think you have hidden the conditional logic into your AddAllowedChange framework. These are notoriously difficult to deal with in OO code, so I would prefer introducing polymorphism here.

Normally, to get rid of the enumeration, we would introduce classes for each value and use the type as the differentiating feature rather than the number. Reviewing your code, you’ve actually already introduced these classes but you are still using the enum. I’m not sure why.

For the purposes of your code, what is the difference between:

public class PauseState : StateHandler<MakiGame.MakiGameState>
{
    public MakiGame Game;
    public PauseState()
        : base(MakiGame.MakiGameState.Paused)
    {    }
}

and

public class PauseState : StateHandler
{
    public MakiGame Game;
    public PauseState()
    {    }
}

?

I simply removed the enumeration but I haven’t removed the difference between PauseState and, say, InGameState:

public class InGameState : StateHandler
{
    public MakiGame Game;
    public InGameState()
    {    }
}

You can differentiate between these classes without an enum because their type is different. You can also keep with your AddAllowedChange framework by referring to types rather than enums:

intro.AddAllowedChange(typeof(InGameState));

However, one of the primary reasons we like to get rid of enums in favor of classes is so we can introduce logic directly on the state we were describing with enums. Rather than doing the setup of allowed changes in the game startup, it might make sense to move some or all to the class definition. I find this syntax more declarative:

public class PauseState : State
{
    public MakiGame Game;
    public PauseState()
    {    }
    public IEnumerable<State> AllowedChange
    {
      get
      {
        yield return new InGameState();
      }
    }
}

If needed, you can take some other approaches to reduce the memory footprint of creating new classes each time this enumerator is called.

1 Like

Thanks for the feedback really appreciated. I agree with you that StateCode is basically behaving like a magic number.

One of the reasons I did add StateCode is that the AddAllowedChange and CanChange methods are actually backed internally by a HashSet which is super quick and has a very low memory footprint (this is a mobile game - forum link in signature)

public abstract class StateHandler<T> where T: struct, IConvertible
{
    public readonly T StateCode;
    public abstract void Start(StateHandler<T> _oldState);
    public abstract void Stop(StateHandler<T> _newState);
    protected HashSet<T> mAllowedChanges;

    static StateHandler()
    {
        if(typeof(T).IsEnum == false)
        {
            throw new ArgumentException("StateHandler T must be of type enum");
        }
    }

    public void AddAllowedChange(T _state)
    {
        mAllowedChanges.Add(_state);
    }

    public void RemoveAllowedChange(T _state)
    {
        mAllowedChanges.Remove(_state);
    }

    public bool CanChange(T _newState)
    {
        return mAllowedChanges.Contains(_newState);
    }

    public StateHandler(T _code)
    {
        mAllowedChanges = new HashSet<T>();
        StateCode = _code;
    }
}

If I was to get rid of StateCode, what would you recommend putting in the HashSet?

Would it be possible to get a unique identifier from typeof, something that is lightweight and has a fast method to generate a hash for the HashSet?

I would prefer not to hard code any allowed change list internally in the class as it is possible for allowed state changes to change during game play. The overall game state isn’t a good example, but there are situations with AI and such where it can change.

Anyway thanks a lot for the critique always helps us to become better developers.

Almost all objects have a fast method to generate hashes called GetHashCode(). This includes the type Type.

If you really dig into things, the HashSet constructors accept an IEqualityComparer which is used to create a hash of the object being added to the set. IEqualityComparer.GetHashCode(object) is called to generate the hash for the object. This means you can create a HashSet of any type and still get very good performance if you provide an IEqualityComparer with a fast implementation of GetHashCode(T).

In the Microsoft implementation of .net, by default, enums get a special IEqualityComparer to prevent the enums from getting boxed. I wouldn’t be surprised if calculating the hash codes of an enum are marginally faster than calculating for other kinds of objects but I would be surprised if this made a significant impact on your game’s performance.

For the most part, I think you can use HashSet choosing any value for T without worrying about the speed and performance of the hashes. The main thing is to choose the correct data structure for your task, which I believe you have already done, and ignore them gritty implementation details unless they become a problem.

So, to answer your question, just use HashSet and let the framework deal with adding and removing values.

I anticipated this was the case so I tried to suggest you could move only some of the allowed state changes into the class definitions. You could do this with a backing List which is populated with the default allowed changes in the type’s constructor. However, this might create more segmentation than it’s worth – you’ll need to be the judge of that.