Context: Implementing StateMachines that control character states in a game.

I have an interface named IState.
Implementations of IState register with an implementation of IStateMachine.
In the IStateMachine implementation I need to have two separate collections of IStates.


The reason for this is because some states need to play on top of other states.
For example the block state of the character plays on top of a base state such as GroundedState or AirborneState because the player can be blocking while running or jumping.
I’d like to have two Register methods in the IStateMachine implementation.
One that places an IState implementation in the normal collection of IStates and one that places an IState in the collection of IStates that play on top of a base IState implementation.

A marker interface could do this nicely because I could just have
Register(IState state)
Register(ISubState state)
but everything I read says marker interfaces are a code smell and that I should use attributes.

The problem is I don’t see how attributes help me? Admittedly I have never used them before this but it doesn’t seem like they can be passed in to a method the way an interface can.

Should I add an interface that inherits IStateMachine that has its own RegisterSubState(IState state) method?
I’ve asked myself the same question. It will be interesting to read the answers.
I don’t quite understand what it means to have “some states play on top of other states”, but, instead of multiple lists it could be a dictionary of lists indexed by an enumeration, where the enum is the “layer”. That enum could also be a property defined on the interface and implemented within each state, defining what sort of state it is. The registration would use that to determine which list to add it to.
Play on top of meaning playing at the same time, overriding some parts of the animation like the characters upper-body.
I thought about enums but it seems to me like non sub-states should not even need to be aware of the existence of sub-states which they necessarily would have to be if they had to set an enum.


C# devs
null reference exceptions

source