【问题标题】:How can I create a List of classes with a generic type parameter in c#?如何在 C# 中创建具有泛型类型参数的类列表?
【发布时间】:2015-03-26 17:06:09
【问题描述】:

我正在尝试创建自己的状态机,但在使用泛型类型的类列表方面遇到了一些麻烦。我的代码如下。

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&lt;T&gt;State&lt;TestStateMachine&gt; 替换为State&lt;StateMachine&gt; 并将(Input.GetKeyDown(sm.kc)) 替换为(Input.GetKeyDown(KeyCode.A)),我的状态机“功能”。

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

【问题讨论】:

  • 在哪里你得到错误?你能用更少的代码重现它吗?你能正确缩进你的代码吗?这将使阅读更容易......
  • 我最好的猜测是您在将实例添加到 List> 时遇到了一些问题,但请提供一些说明。
  • 好吧,对于初学者来说,这是:public List&lt;State&lt;T&gt;&gt; stateList = new List&lt;T&gt;&gt;();

标签: c# list generics unity3d state-machine


【解决方案1】:

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

问题是这样的:

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

T 在此类中没有任何意义,因为它不是泛型类或方法。因此,编译器不知道如何处理State&lt;T&gt;List&lt;T

第二个问题是

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

即使在合适的泛型类或方法中也无法编译:List&lt;State&lt;T&gt;&gt; 不是与List&lt;T&gt; 兼容的类型。

【讨论】:

    【解决方案2】:

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

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

    但是,这可能非常令人困惑。如果可以,您应该考虑将 State 设计为非泛型抽象类或接口。

    【讨论】:

    • 嗯,你的方法并不完全奏效,但你的回答给了我一个想法,经过试验,效果很好。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多