簡體   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