1

I have a StateMachine class that needs to receive an initial State value in the constructor so that the the crntState and nextState would not be null at construction.

public class StateMachine {
    State crntState;
    State nextState;

    public StateMachine(State initialState) {
        crntState = initialState;
        nextState = crntState;
    }
}

Problem is that I need to first create the value in the inheriting class's constructor before passing it to the base, since the IdleState constructor requires a reference to MovementSM

class MovementSM : StateMachine {
    Player player;
    IdleState idleState;
    MovingState movingState;

    
    public MovementSM(Player player) : base(/*I want to pass idleState here*/) {
        this.player = player;

        idleState = new IdleState(this); //idleState can be declared only after the call to the base constructor
        movingState = new MovingState(this);
    }
}

I realize that I can initialize crntState and nextState in a seperate function and call it from MovementSM's constructor, but then the compiler gives a warning that the non-nullable fields of crntState and nextState must contain non-null value when exiting the constructor. I could make them nullable, but they don't have any logical reason to be nullable in the program. I could also assign them dummy values in the constructor before assigning them actual values in the separate function, but that just defeats the whole purpose of making sure the fields are being assigned at construction, plus this idea includes a pointless step of assignment to crntState and nextState just to keep the compiler quite...

So with all that considered, what is the best way of addressing that problem?

EDIT: Some people suggested that if I removed the need to pass this to the state constructors, I could pass new IdleState() to the base, and get it back to idleState by doing idleState = crntState. Beside thinking that passing this is the correct solution in my case (explanation in the comments), I think passing the value first to the base could be really ugly. For exmaple, I have this same problem with an Animation instance that I wanted to pass from a class to it's base, and it looks like that:

walk = new Animation(new[]{
    (new IntRect(16, 0, 16, 32), new Vector2i(0, 0), 0.2f),
    (new IntRect(32, 0, 16, 32), new Vector2i(0, 0), 0.2f),
    (new IntRect(16, 0, 16, 32), new Vector2i(0, 0), 0.2f),
    (new IntRect( 0, 0, 16, 32), new Vector2i(0, 0), 0.2f),
});

Putting all that in a the base call just to later do walk = crntAnimation is really ugly in my opinion. Even more so, what if I wanted to generate that value using a for loop for example? Then passing it to base first would not only be ugly, but impossible.

Lodea
  • 119
  • 12
  • 2
    Can't do it, you will need an init method , or rethink your problem. – TheGeneral May 21 '22 at 11:42
  • 1
    If you're only concerned about nullability, you can easily work around that by initializing the fields to `default!`, i.e. `State crntState = default!`. At that point, you're basically telling the compiler to trust you that you know what you're doing and have ensured that it will not in fact be null when those fields are consumed. – Kirk Woll May 21 '22 at 11:52
  • 2
    Passing `this` in the constructor of your `State`s seems suspect, and eliminating this fixes the problem. You are creating circular dependencies. It is hard to imagine why your states need to have a reference to the state machine that they exist within. Also, for your design to work, the states will either have to be immutable and references updated explicitly or have to be `struct` rather than `class` to ensure your `crntState` can be different than `nextState`. – NightOwl888 May 21 '22 at 12:00
  • The _fastest_ way to shut up the compiler is Kirk's. The _best_ way is certainly opinionated, and in my opinion, you want to redesign the entanglement of `MovementSM` requiring `IdleState` and `MovingState` with them again requiring an instance of `MovementSM`. – Ray May 21 '22 at 12:01
  • You can pass `null!` to suppress the warning and initialize fields later. – shingo May 21 '22 at 12:35
  • @NightOwl888 @Ray The reason I'm passing `MovementSM` to `IdleState` and `MovingState` is that they require using a lot of `MovementSM`'s functionality and fields. I'm aware I could pass a bunch of lambdas to the constructor instead, but I found that to be really messy. Instead I created `IdleState` and `MovingState` as nested classes inside `MovementSM` so that they'd have access to all of it's members without having to make them all public. Besides, even If I didn't have to pass `this`, passing `new IdleState()` to base, and then make `idleState = state` is pretty ugly. (explanation in edit) – Lodea May 21 '22 at 13:59
  • @user5455109 - That still doesn't solve the issue I mentioned where your state classes will need to be immutable and you must edit update the `nextState` reference explicitly. Whenever I find myself fighting the tide to force a pattern into a box it doesn't want to fit in, I usually double-check to see if there is another design pattern that fits the scenario. Is state machine the right pattern for the job? Without more info, we cannot answer. However, it would be wise to look at some [example implementations of state machines](https://stackoverflow.com/q/5923767) to see if they fit. – NightOwl888 May 21 '22 at 16:29

0 Answers0