想象以下类:
public abstract class State {}
public abstract class View : MonoBehaviour {
public State state;
}
现在我想从两个类派生来创建StateA
和ItemA
。
public class StateA { // some properties }
public class ViewA : View {
public StateA state;
}
ViewA
只应接受StateA
作为其状态。不应该设置假设的StateB
。当我尝试设置ViewA
View view = someGameObject.GetComponent<View>;
view.state = new StateA ();
仅设置基类的状态。在我的情况下,投射到ViewA
实际上是不可能的,因为它必须动态投射。
现在我提出了两种可能的解决方案。
解决方案1 - 键入检查并投射
所有州级别保持不变。基类正在检查状态的正确类型。
public abstract class View : MonoBehaviour {
public State state;
public Type stateType;
public void SetState (State state) {
if (state.GetType () == this.stateType) {
this.state = state;
}
}
}
要访问ViewA
中的状态,请执行以下操作:
(StateA)this.state
解决方案2 - 统一方式
State
将更改为从MonoBehaviour派生。
public abstract class State : MonoBehaviour {}
要向项添加状态,我只需将新的StateA
组件添加到gameObject。然后该项使用GetComponent加载该状态以访问它。
viewA.gameObject.AddComponent<StateA> (); // set state with fixed type
viewA.gameObject.AddComponent (type); // set state dynamically
this.gameObject.GetComponent<StateA> (); // get state in item class
结论
在我看来,这两种解决方案都不理想。你们知道更好的方法来做这种事吗?我希望你明白我想要实现的目标。期待看到你的想法。
修改
似乎我过分简化了我的问题。为了明确我为什么这样做,在UI中将Item
视为View
。 StateA
没有任何功能。相反,它只是一些属性让视图知道它应该做什么。例如,对于表视图,状态将包含要显示的事物的集合。
public class StateA : State {
SomeObject[] tableViewData; // which data to load into the table view
}
或者另一种观点。
public class StateB : State {
bool hideButtonXY; //should I hide that button?
}
在另一个课程中,我保留了最后显示的视图与各自状态的历史记录。这样我就可以从普通的Web浏览器实现来回功能。 我希望这能使我的意图明确。
答案 0 :(得分:3)
这是一个相当普遍的问题,它是由需要在Unity中使用的奇怪方式MonoBehaviours和常见的继承误用造成的。这不是你可以解决的问题,而是你为了避免而设计的东西。
这一切都取决于你为什么需要State的子类,但我假设它是这样的:base state有一些基本逻辑,派生的StateA有一些新的/修改过的属性/逻辑。假设这是真的:
代码中写出的示例:
abstract class State {
public int sharedField;
public sealed void DoThing() {
sharedField = 1;
DoThingInternal();
}
protected abstract void DoThingInternal();
}
abstract class Item {
State state;
public abstract void TestMethod();
}
class StateA : State {
public int customField = 10;
protected override void DoThingInternal() {
sharedField = 10;
}
}
class ItemA : Item {
public ItemA() {
//read from somewhere else, pass it in, etc.
state = new StateA();
}
public override void TestMethod() {
state.DoThing();
Console.WriteLine(state.sharedField); //will print 10
}
}
这是一种非常常见的模式,特别是在Unity中,当你想要使用这样的继承时。这种模式在Unity之外也非常有效,并且避免了泛型,因此通常可以知道。
注意:公共方法的密封属性的目的是防止其他人/你自己以后试图隐藏它(public new void DoThing()或public void DoThing())然后在它无法工作时感到困惑。
如果你要覆盖它,那么因为你通过 base 类调用它,它会调用函数的 base 版本而不是你的新版本方法。将它定义为抽象/虚拟并由基类调用本身就可以解决这个问题。这确实意味着您已经有效地定义了:
interface IState {
public int sharedField;
public void DoThing();
}
因此,您可以看到使用界面可以实现相同的结果并注意实现。
编辑以符合示例:
abstract class State {
public sealed Button[] GetHiddenButtons() {
GetHiddenButtonsInternal();
}
public sealed object[] GetObjects() {
GetObjectsInternal();
}
protected abstract Button[] GetHiddenButtonsInternal();
protected abstract object[] GetObjects();
}
abstract class Item {
State state;
public abstract void Render();
}
class StateA : State {
public Button[] _hiddenButtons;
public object[] _objects;
public StateA()
{
//get hidden buttons from somewhere/pass them in/create them.
_hiddenButtons = new Button[0];
//get objects from somewhere/pass them in/create them.
_objects = new object[0];
}
protected override Button[] GetHiddenButtons() {
return _hiddenButtons;
}
protected override object[] GetObjects() {
return _objects;
}
}
class ItemA : Item {
public ItemA() {
//read from somewhere else, pass it in, etc.
state = new StateA();
}
public override void Render() {
//get objects
var objects = state.GetObjects(); //returns array from StateA
//get hidden buttons to hide
var hiddenButtons = state.GetHiddenButtons();
}
}
基本上,您正在做的是创建一个接口,它足够通用,可以满足您所需要的状态。该模式并未规定您需要如何返回它们,因此您的GetHiddenButtons()方法可以:返回ID列表,返回对象,联系GameObject以获取列表,返回硬编码列表等。
这种模式可以让你完成你想要做的事情,你只需要确保基本状态的设计通常足以让你这样做。