gpt4 book ai didi

c# - 将基类属性覆盖为派生类型并通过基类访问

转载 作者:行者123 更新时间:2023-11-30 21:41:33 24 4
gpt4 key购买 nike

想象以下类:

public abstract class State {}

public abstract class View : MonoBehaviour {
public State state;
}

现在我想从这两个类派生以创建 StateAItemA

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 组件。然后该项目使用 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

结论

在我看来,这两种解决方案都不理想。你们知道做这种事情的更好方法吗?我希望你明白我想要达到的目标。期待看到您的想法。

编辑

看来我把问题简单化了。为了弄清楚为什么我这样做,请将 Item 视为 UI 中的 ViewStateA 没有任何功能。相反,它只是一些让 View 知道它应该做什么的属性。例如,对于 TableView ,状态将包含要显示的事物的集合。

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?
}

在另一个类中,我保留了最后显示的 View 及其各自状态的历史记录。通过这种方式,我可以从普通网络浏览器实现来回功能。我希望这能表明我的意图。

最佳答案

这是一个相当普遍的问题,由需要在 Unity 中使用 MonoBehaviours 的奇怪方式和常见的继承误用共同引起。这不是您可以解决的问题,而是您为了避免而设计的问题。

这完全取决于您为什么需要 State 的子类,但我假设它类似于:基本 State 具有一些基本逻辑,而派生的 StateA 具有一些新的/修改的属性/逻辑。假设这是真的:

  • 使用对 State 的相同引用来保留所有 Item 子类。因为它是一个子类,所以您可以将它保存到基类型的属性中。
  • 制定一组标准的访问器/字段/方法,这些访问器/字段/方法是 Item 子类始终访问 State 子类的方式。示例:DoThing()、ProcessState() 等。
  • 使这些访问器/字段/方法具有将始终在密封函数中运行的通用代码。
  • 让上述方法调用一个内部/ protected 方法,该方法定义您需要运行的任何自定义逻辑。如果您想提供它们的标准实现,它们应该是抽象的或虚拟的。
  • 让所有子类只覆盖抽象/虚拟方法,然后它们就可以访问任何自定义代码。
  • 当您调用 stateStoredAsBaseClass.DoThing() 时,它会自动调用 stateStoredAsBaseClass.DoThingInternal(),它在子类中被覆盖,因此其中包含您的自定义 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 方法中 sealed 属性的作用是防止其他人/你自己以后试图隐藏它(public new void DoThing() 或只是 public void DoThing())然后在它不起作用时感到困惑。

如果您要覆盖它,那么因为您通过 base 类调用它,它会调用该函数的 base 版本而不是您的新方法。将它定义为抽象/虚拟并由基类本身调用可以解决这个问题。这确实意味着您已经有效地定义了:

interface IState {
public int sharedField;

public void DoThing();
}

因此您可能会看到,使用接口(interface)并注意实现也可以实现相同的结果。

编辑使示例符合问题:

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 列表、返回对象、联系游戏对象以获取列表、返回硬编码列表等。

此模式允许您完全按照自己的意愿行事,您只需确保基本状态的设计足够通用,以允许您这样做。

关于c# - 将基类属性覆盖为派生类型并通过基类访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43241213/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com