Keys
Keys

Reputation: 3

how to make class type safe

using System;
using System.Reflection;
using UnityEngine;

public class StateMachine<TStates> where TStates : Enum
{
    private TStates initialState;
    private TStates currentState;
    private MonoBehaviour component;

    public StateMachine(MonoBehaviour component)
    {
        this.component = component;
    }
    public StateMachine(MonoBehaviour component, TStates initialState)
    {
        this.component = component;
        this.initialState = initialState;

        SetState(this.initialState);
    }
    
    public void SetState(TStates newState)
    {
        Type componentType = component.GetType();
 
        if (currentState != null)
        {
            if (currentState.Equals(newState))
                return;
                
            componentType.GetMethod("On" + currentState + "Exit", BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(component, null);
        }
        else
            initialState = newState;

        currentState = newState;

        componentType.GetMethod("On" + currentState + "Enter", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(component, null);
    }
    public void Update()
    {
        Type componentType = component.GetType();

        if (currentState != null)
            componentType.GetMethod("On" + currentState + "Update", BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(component, null);
    }
    public void Reset()
    {
        if (initialState != null)
            SetState(initialState);
    }
}

I have a finite state machine that I tried making myself and it works fine. However, people told me that it isn't type safe.

They told me to use switch statements but I don't see how I'm able to implement them.

Any way I can make it type safe?

Upvotes: 0

Views: 79

Answers (3)

Ivan Todorov
Ivan Todorov

Reputation: 1

You can easily make the state machine type-safe by introducing a common base class or an interface, which your states should inherit from. Given that, implementing the state machine design pattern is pretty straightforward:

public interface IState
{
    void Handle(Context context);
}

public class Walk : IState
{
    public void Handle(Context context)
    {
        context.State = new Shoot();
    }
}

public class Shoot : IState
{
    public void Handle(Context context)
    {
        context.State = new Walk();
    }
}

public class Context
{
    public Context(IState state)
    {
        this.State = state;
    }

    public IState State { get; set; }

    public void Request()
    {
        this.State.Handle(this);
    }
}

Upvotes: 0

TimChang
TimChang

Reputation: 2417

public interface IStateHandle<TStates> where TStates : Enum
{
    void OnEnter(TStates state);
    void OnUpdate(TStates state);
    void OnExit(TStates state);
}

public class StateMachine<TStates> where TStates : Enum
{
    IStateHandle<TStates> _handle;
    TStates _currentState;
    public StateMachine(IStateHandle<TStates> handle)
    {
        _handle = handle;
    }

    public void Update()
    {
        _handle.OnUpdate(_currentState);
    }
}

Upvotes: 1

JonasH
JonasH

Reputation: 36361

You could for example replace your state with a state type that just uses methods, something like:

public interface IState{
    void Exit();
    void Enter();
    void Update();
}

And provide an implementation for each state you want. You could also have a implementation that delegates the implementation of each method to a delegate.

Upvotes: 0

Related Questions