繁体   English   中英

将基类属性重写为派生类型,并通过基类进行访问

[英]Override base class property to a derived type and access via base class

想象以下几类:

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转换为ViewA ,因为必须动态地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的状态, 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中的“ 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浏览器实现来回功能。 我希望这可以使我的意图明确。

这是一个相当普遍的问题,是由于在Unity中需要使用MonoBehaviours的奇怪方式和对继承的普遍误用所造成的。 这不是您可以解决的事情,而是您为了避免而设计的事情。

这一切都取决于您为什么需要State的子类,但是我假设它是类似的:base 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方法上的密封属性的要点是防止其他人/您自己稍后再尝试隐藏它(public new void DoThing()或只是public void DoThing()),然后在不起作用时感到困惑。

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

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联系以获取列表,返回硬编码列表等。

这种模式使您可以完全按照自己的意愿去做,只需要确保基本状态的通用性足以允许您去做即可。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM