C#:继承,覆盖和隐藏

时间:2010-04-08 02:39:49

标签: c# oop architecture inheritance syntax

我对C#XNA游戏的架构决策有困难。

世界上的基本实体,例如树,僵尸或玩家,被表示为GameObject。每个GameObject至少由GameObjectControllerGameObjectModelGameObjectView组成。

对于简单的实体,如无生命的树木或岩石,这三个就足够了。但是,当我尝试尽可能地保留功能时,继承开始变得难以处理。从语法上讲,我甚至不确定如何最好地实现我的目标。

以下是GameObjectController

public class GameObjectController
{
    protected GameObjectModel model;

    protected GameObjectView view;

    public GameObjectController(GameObjectManager gameObjectManager)
    {
        this.gameObjectManager = gameObjectManager;
        model = new GameObjectModel(this);
        view = new GameObjectView(this);
    }

    public GameObjectManager GameObjectManager
    {
        get
        {
            return gameObjectManager;
        }
    }

    public virtual GameObjectView View
    {
        get
        {
            return view;
        }
    }

    public virtual GameObjectModel Model
    {
        get
        {
            return model;
        }
    }

    public virtual void Update(long tick) 
    {
    }
}

我想指定GameObjectController的每个子类至少可以访问GameObjectViewGameObjectModel。如果使用这些类的子类很好,但可能要覆盖更复杂的Update()方法,我不希望他们必须复制代码来产生这些依赖关系。因此,GameObjectController构造函数设置了这些对象。

但是,某些对象确实要覆盖模型和视图。这就是问题所在。

有些物品需要战斗,所以它们是CombatantGameObjects

public class CombatantGameObject : GameObjectController
{
    protected new readonly CombatantGameModel model;
    public new virtual CombatantGameModel Model
    {
        get { return model; }
    }

    protected readonly CombatEngine combatEngine;

    public CombatantGameObject(GameObjectManager gameObjectManager, CombatEngine combatEngine)
        : base(gameObjectManager)
    {
        model = new CombatantGameModel(this);
        this.combatEngine = combatEngine;
    }

    public override void Update(long tick)
    {
        if (model.Health <= 0)
        {
            gameObjectManager.RemoveFromWorld(this);
        }
        base.Update(tick);
    }
}

还很简单。我使用new隐藏实例变量是否正确?请注意,即使CombatantObjectController.model已设置,我也在此处指定GameObjectController.Model。而且,战斗员不需要任何特殊的视图功能,所以他们只留下GameObjectController.View

然后我到达PlayerController,发现了一个错误。

public class PlayerController : CombatantGameObject
{
    private readonly IInputReader inputReader;

    private new readonly PlayerModel model;
    public new PlayerModel Model
    {
        get { return model; }
    }

    private float lastInventoryIndexAt;
    private float lastThrowAt;

    public PlayerController(GameObjectManager gameObjectManager, IInputReader inputReader, CombatEngine combatEngine)
        : base(gameObjectManager, combatEngine)
    {
        this.inputReader = inputReader;
        model = new PlayerModel(this);
        Model.Health = Constants.PLAYER_HEALTH;
    }

    public override void Update(long tick)
    {
        if (Model.Health <= 0)
        {
            gameObjectManager.RemoveFromWorld(this);
            for (int i = 0; i < 10; i++)
            {
                Debug.WriteLine("YOU DEAD SON!!!");
            }
            return;
        }

        UpdateFromInput(tick);
        // ....
    }
   }

第一次执行此行时,我得到一个空引用异常:

model.Body.ApplyImpulse(movementImpulse, model.Position);

model.Position查看model.Body,其为空。

这是一个在将GameObjects部署到世界之前初始化它的函数:

   public void Initialize(GameObjectController controller, IDictionary<string, string> data, WorldState worldState)
    {
        controller.View.read(data);
        controller.View.createSpriteAnimations(data, _assets);

        controller.Model.read(data);

        SetUpPhysics(controller,
         worldState,
         controller.Model.BoundingCircleRadius,
         Single.Parse(data["x"]),
         Single.Parse(data["y"]), bool.Parse(data["isBullet"]));
    }

每个对象都以GameObjectController传递。这是否意味着如果对象真的是PlayerControllercontroller.Model将引用基数GameObjectModel而不是PlayerController覆盖PlayerObjectModel

回应rh:

  

这意味着现在为PlayerModel   p,p.Model不等同于   ((CombatantGameObject)p).Model,和   也不等同于   ((GameObjectController)P)。型号

这正是我不想要的。我想要:

PlayerController p;
p.Model == ((CombatantGameObject)p).Model
p.Model == ((GameObjectController)p).Model

我该怎么做? override

1 个答案:

答案 0 :(得分:2)

关键在于您在此处使用'new'关键字:

private new readonly PlayerModel model;
public new PlayerModel Model
{
    get { return model; }
}

在这里:

protected new readonly CombatantGameModel model;
public new virtual CombatantGameModel Model
{
    get { return model; }
}

你所说的是:“我知道我的基类已经定义了这些,但我想定义不同的碰巧具有相同名称的那些。”

这意味着现在对于PlayerModel p,p.Model 等同于((CombatantGameObject)p).Model,并且 not 等效于((GameObjectController) )p)。型号

以下是您可以采取的几种方法。

1)不要提供强类型的子属性。 我知道这可能听起来很糟糕,但它实际上是一个非常强大的概念。如果基础模型定义了适用于所有子类的相应抽象/虚拟方法,则可以在基类中定义属性一次并完成。然后子类可以提供自己的实现。

以下是实现此目的的一种可能方法。

public class GameObjectController /* ... */
{
    /* ... */
    public GameObjectController()
    {
        Model = new GameObjectModel(this);
    }

    public GameObjectModel Model { get; protected set; }
}

public class CombatantGameObject : GameObjectController
{
    /* ... */
    public CombatantGameObject()
    {
        Model = new CombatantModel(this);
    }
}
/* ... */

2)通过子类访问时提供强类型属性,但将字段存储在基类中一次。

这可行,但正确做法很棘手。这不是我的第一选择。

public class GameObjectController /* ... */
{
    /* ... */
    public GameObjectController()
    {
        Model = new GameObjectModel(this);
    }

    public GameObjectModel Model { get; protected set; }
}

public class CombatantGameObject : GameObjectController
{
    /* ... */
    public CombatantGameObject()
    {
        Model = new CombatantModel(this);
    }

    public new CombatantModel Model
    {
        get
        {
            return (CombatantModel)base.Model;
        }
        protected set
        {
            base.Model = value;
        }
    }
}
/* ... */

另外,要过早对过度提交复杂对象模型持谨慎态度。有时候开始简单,并且积极地进行重构,这是获得一个反映代码实际最终工作方式的好模型的最佳方式。