Tan Edmond
Tan Edmond

Reputation: 1

How can I create a List of classes with a generic type parameter in c#?

I'm trying to create my own state machine but ran into some trouble regarding lists of classes with generic type. My code is as below.

State.cs:

using UnityEngine;
using System.Collections;

public abstract class State<T> where T:StateMachine
{

  public T sm;

  public State()
  {
  }

  public virtual void OnEnter()
  {
    sm.currentState = sm.futureState;
  }

  public abstract void OnExit();
  public abstract void OnLoop();

}

StateMachine.cs:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public abstract class StateMachine : MonoBehaviour
{
  public List<State<T>> stateList = new List<T>>();
  public int currentState = -1;
  public int futureState;

  protected virtual void Start()
  {
    foreach (State<T> s in stateList)
    {
      s.sm = this;
    }
  }

  protected virtual void Update()
  {
    if (currentState != futureState)
    {
      stateList[futureState].OnEnter();
    }

    stateList[currentState].OnLoop();

    if (currentState != futureState)
    {
      stateList[currentState].OnExit();
    }

  }

}

TestStateMachine.cs:

using UnityEngine;
using System.Collections;

public class TestStateMachine : StateMachine
{

  public enum StateNames:int
  {
    State1,
    State2,
  };

  public KeyCode kc;

  // Use this for initialization
  protected override void Start ()
  {
    stateList.Add(new TestStateMachineFirstState());
    stateList.Add(new TestStateMachineSecondState());
    base.Start();
  }

}

public class TestStateMachineFirstState : State<StateMachine>
{

  public override void OnEnter()
  {
    Debug.Log("SM1 OnEnter");
    base.OnEnter();
  }

  public override void OnLoop()
  {
    Debug.Log("SM1 OnLoop");

    if (Input.GetKeyDown(sm.kc))
    {
      sm.futureState = (int)TestStateMachine.StateNames.State2;
    }

  }

  public override void OnExit()
  {
    Debug.Log("SM1 OnExit");
  }

}

public class TestStateMachineSecondState : State<StateMachine>
{

  public override void OnEnter()
  {
    Debug.Log("SM2 OnEnter");
    base.OnEnter();
  }

  public override void OnLoop()
  {
    Debug.Log("SM2 OnLoop");

    if (Input.GetKeyDown(sm.kc))
    {
      sm.futureState = (int)TestStateMachine.StateNames.State1;
    }

  }

  public override void OnExit()
  {
    Debug.Log("SM2 OnExit");
  }

}

I get error CS0246: Type or namespace name T cannot be found (or something that sounds similar).

My state machine "functions" if I replace all State<T> and State<TestStateMachine> with State<StateMachine> and the if (Input.GetKeyDown(sm.kc)) with (Input.GetKeyDown(KeyCode.A)).

But that is not ideal as I would not be able to get variables from the children state machines. Is there a way to keep this structure(as bad as it might be), or should I try another approach in doing state machines?

Upvotes: 0

Views: 122

Answers (2)

Bas
Bas

Reputation: 27105

The reason you get this compilation error is because you are using type parameter T of type State in the StateMachine class. You could employ the curiously recurring template pattern:

class State<T> where T : StateMachine
class StateMachine<T> where T : StateMachine
class RealStateMachine : StateMachine<RealStateMachine>

However, this might be very confusing. If you are able, you should consider a design where State is a nongeneric abstract class or interface.

Upvotes: 0

Nicholas Carey
Nicholas Carey

Reputation: 74277

One might note that if you look at the compiler error message, it will specify the source file and the line number at which the error was detected. That usually helps identify the problem.

The problem is this:

public abstract class StateMachine : MonoBehaviour
{
  public List<State<T>> stateList = new List<T>>();
  ...

T has no meaning in this class as it's not an generic class or method. Hence, the compiler has no idea what to do with State<T> or List<T.

A second problem is that

public List<State<T>> stateList = new List<T>>();

wouldn't compile even if in a suitable generic class or method: List<State<T>> is not a compatible type with List<T>.

Upvotes: 1

Related Questions