gpt4 book ai didi

C#:继承、覆盖和隐藏

转载 作者:太空宇宙 更新时间:2023-11-03 11:48:54 26 4
gpt4 key购买 nike

我在为我的 C# XNA 游戏做出架构决策时遇到困难。

世界中的基本实体,例如树、僵尸或玩家,都表示为游戏对象。每个 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 构造函数会设置这些对象。

但是,有些对象确实想要覆盖模型和 View 。这就是问题所在。

一些对象需要战斗,所以它们是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 已经设置。而且,战斗人员不需要任何特殊的 View 功能,因此他们不用理会 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.Positionmodel.Body,为null。

这是一个在游戏对象被部署到世界之前初始化游戏对象的函数:

   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:

This means that now for a PlayerModel p, p.Model is not equivalent to ((CombatantGameObject)p).Model, and also not equivalent to ((GameObjectController)p).Model.

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

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

我该怎么做? 覆盖?

最佳答案

关键在于你在这里使用了'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,并且等同于 ((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;
}
}
}
/* ... */

另外,要警惕过早地过度致力于复杂的对象模型。有时,从简单开始并积极重构,是获得反射(reflect)代码最终实际工作方式的良好模型的最佳方式。

关于C#:继承、覆盖和隐藏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2597243/

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