如何在c#中创建具有泛型类型参数的类列表?

时间:2015-03-26 17:06:10

标签: c# list generics unity3d state-machine

我正在尝试创建自己的状态机,但是遇到有关泛型类的列表的麻烦。我的代码如下。

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");
  }

}

我收到错误CS0246: Type or namespace name T cannot be found(或听起来类似的内容)。

如果我将所有State<T>State<TestStateMachine>替换为State<StateMachine>,将(Input.GetKeyDown(sm.kc))替换为(Input.GetKeyDown(KeyCode.A)),我的状态机将“起作用”。

但这并不理想,因为我无法从子状态机中获取变量。有没有办法保持这种结构(尽可能糟糕),或者我应该尝试另一种方法来做状态机?

2 个答案:

答案 0 :(得分:1)

有人可能会注意到,如果查看编译器错误消息,它将指定源文件和检测到错误的行号。这通常有助于确定问题。

问题在于:

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

T在此课程中没有任何意义,因为它不是通用的类或方法。因此,编译器不知道如何处理State<T>List<T

第二个问题是

public List<State<T>> stateList = new List<T>>();
即使在合适的通用类或方法中,

也不会编译:{{1​​}}不是与List<State<T>>兼容的类型。

答案 1 :(得分:0)

您收到此编译错误的原因是您在T类中使用类型State的类型参数StateMachine。您可以使用curiously recurring template pattern

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

然而,这可能会令人困惑。如果能够,则应考虑State是非泛型抽象类或接口的设计。