C#多态性问题再次出现-覆盖字段?

C#多态性问题再次出现-覆盖字段?,c#,xna,polymorphism,C#,Xna,Polymorphism,这次我遇到了虚拟字段的问题 我有我的游戏对象的核心类。此类包含具有模型类对象的字段。模型的对象包含位置等值 现在-在绘图时,我需要从模型中读取每个对象的位置。当我使用派生类而不是默认模型类时,问题就开始了。例如: abstract class GenericGameObject { public DefaultGameObjectModel Model = new DefaultGameObjectModel(); } class Missile : GenericGameObject { pu

这次我遇到了虚拟字段的问题

我有我的游戏对象的核心类。此类包含具有模型类对象的字段。模型的对象包含位置等值

现在-在绘图时,我需要从模型中读取每个对象的位置。当我使用派生类而不是默认模型类时,问题就开始了。例如:

abstract class GenericGameObject { public DefaultGameObjectModel Model = new DefaultGameObjectModel(); }
class Missile : GenericGameObject { public new MissileModel Model = new MissileModel(); }

class DefaultGameObjectModel { public Vector2 Position = new Vector2(){X=0}; }
class MissileModel : DefaultGameObjectModel { }

Missile m = new Missile();
m.Model.Position.X = 10;
// NOT OK! ((GenericGameObject)m).Model.Position.X == 0
我试图将模型定义为虚拟财产而不是字段,但失败了,因为 派生属性必须与其基属性的类型相同。铸造是徒劳的,因为将有许多其他模型类型。如果我想从派生类而不是从基类读取值,我该怎么办

我已经问过这个问题了,但答案没有带来任何解决方案。解释:

  • 使用接口IGameObjectModel

    概念很好,但我必须强制执行字段。接口不能定义字段,所以我必须定义属性。但是我不能做IGameObjectModel.Position.X=10,因为Position不是一个字段

  • 使GenericGameObject成为泛型类型,如GenericGameObject和从GenericGameObject派生的导弹类型 然后,我不能向GenericGameObject投射一枚导弹,并且通常将这些对象存储在同一个列表中。当然,我可以创建这两个可以继承的主基类型,但这样我就无法访问模型字段

  • 使模型成为属性而不是字段。 无法更改派生类中的属性类型


我能做什么?

如果您使用了接口,我相信您仍然可以调用:

IGameObjectModel.Position.X = 10;
只要用于Position的对象类型具有一个名为X的读/写属性,那么您的接口将如下所示:

public interface IGameObjectModel
{
    Vector2 Position
    {
        get;
        // only add set if you need to set the Position object outside of your class
        // set;
    }

    // ...other properties
}

如果使用接口,我相信您仍然可以调用:

IGameObjectModel.Position.X = 10;
只要用于Position的对象类型具有一个名为X的读/写属性,那么您的接口将如下所示:

public interface IGameObjectModel
{
    Vector2 Position
    {
        get;
        // only add set if you need to set the Position object outside of your class
        // set;
    }

    // ...other properties
}

在这种情况下,最好的方法是将父字段的值指定为派生类的实例,然后将其强制转换回派生类或保留派生类的引用(可能更好)

或者你可以沿着这条路走,我最喜欢这条路

abstract class GenericGameObject
{
    public DefaultGameObjectModel Model
    {
        get { return ModelInternal; }
    }

    protected abstract DefaultGameObjectModel ModelInternal { get; }
}

class Missile : GenericGameObject
{
    private MissileModel model = new MissileModel();

    public override DefaultGameObjectModel ModelInternal
    {
        get { return model; }
    }

    public new MissileModel Model
    {
        get { return model; }
        set { model = value; }
    }
}

class DefaultGameObjectModel { public Vector2 Position = new Vector2(){X=0}; }
class MissileModel : DefaultGameObjectModel { }

Missile m = new Missile();
m.Model.Position.X = 10;

此解决方案允许您从基类的上下文访问基类实例,同时允许您从继承的类访问具体的模型实例。

在这种情况下,最好的方法是将父字段的值指定为派生类的实例,然后将其转换回派生类,或者保留派生类的引用(可能更好)

或者你可以沿着这条路走,我最喜欢这条路

abstract class GenericGameObject
{
    public DefaultGameObjectModel Model
    {
        get { return ModelInternal; }
    }

    protected abstract DefaultGameObjectModel ModelInternal { get; }
}

class Missile : GenericGameObject
{
    private MissileModel model = new MissileModel();

    public override DefaultGameObjectModel ModelInternal
    {
        get { return model; }
    }

    public new MissileModel Model
    {
        get { return model; }
        set { model = value; }
    }
}

class DefaultGameObjectModel { public Vector2 Position = new Vector2(){X=0}; }
class MissileModel : DefaultGameObjectModel { }

Missile m = new Missile();
m.Model.Position.X = 10;

此解决方案允许您从基类的上下文访问基本模型实例,同时允许您从继承的类访问具体的模型实例。

您说过,如果您使用了具有属性的接口,则“无法执行IGameObjectModel.Position.X=10”。我假设这是因为Vector2是一个结构,因此具有值类型语义。如果这是正确的,则只需将“位置”特性指定给根据原始值计算的新矢量2即可。例如:

Missile m = new Missile();
m.Model.Position = new Vector2()
{
    X = m.Model.Position.X + 10,
    Y = m.Model.Position.Y
};

您说过,如果您使用了一个属性为“不能执行IGameObjectModel.Position.X=10”的接口。我假设这是因为Vector2是一个结构,因此具有值类型语义。如果这是正确的,则只需将“位置”特性指定给根据原始值计算的新矢量2即可。例如:

Missile m = new Missile();
m.Model.Position = new Vector2()
{
    X = m.Model.Position.X + 10,
    Y = m.Model.Position.Y
};

没有所谓的“虚拟场”。只有和可以是虚拟的

在Missle类中,您似乎正在使用隐藏名为Model的继承成员

以这种方式隐藏继承成员时,不会出现多态行为。这是不好的,因为基类中的代码(如果它引用模型字段)可能无法按预期工作


最佳选择:使用属性。必要时强制转换或泛化(将成员移动到基类)。

没有“虚拟字段”这样的东西。只有和可以是虚拟的

在Missle类中,您似乎正在使用隐藏名为Model的继承成员

以这种方式隐藏继承成员时,不会出现多态行为。这是不好的,因为基类中的代码(如果它引用模型字段)可能无法按预期工作


最佳选择:使用属性。必要时强制转换或泛化(将成员移动到基类)。

是否尝试使用泛型?使用泛型,您可以将游戏对象模型与游戏对象分离。然后,您可以使用任何游戏对象模型实例化游戏对象。游戏对象可以通过标准接口与游戏对象模型通信

interface IGameObjectModel {
    void Shoot();
        :
}

class GameObject<TModel> where TModel:IGameObjectModel {
    public TModel Model;
    public GameObject(TModel model) {
        Model = model;
    }
    public void Shoot() {
        Model.Shoot();
    }
        :
}

class MissleModel : IGameObjectModel {
    public void Shoot() {
        :
    }   
}
接口IGameObjectModel{
空射();
:
}
类GameObject,其中TModel:IGameObjectModel{
公共TModel模型;
公共游戏对象(TModel模型){
模型=模型;
}
公开射击(){
Model.Shoot();
}
:
}
类MissleModel:IGameObjectModel{
公开射击(){
:
}   
}
通过上述步骤,您可以使用导弹模型实例化游戏对象:-

MissleModel model = new MissleModel();
GameObject<MissleModel> obj =
    new GameObject<MissleModel>(model);
MissleModel model=新MissleModel();
游戏对象对象=
新游戏对象(模型);

您是否尝试使用泛型?使用泛型,您可以将游戏对象模型与游戏对象分离。然后,您可以使用任何游戏对象模型实例化游戏对象。游戏对象可以通过标准接口与游戏对象模型通信

interface IGameObjectModel {
    void Shoot();
        :
}

class GameObject<TModel> where TModel:IGameObjectModel {
    public TModel Model;
    public GameObject(TModel model) {
        Model = model;
    }
    public void Shoot() {
        Model.Shoot();
    }
        :
}

class MissleModel : IGameObjectModel {
    public void Shoot() {
        :
    }   
}
接口IGameObjectModel{
空射();
:
}
类GameObject,其中TModel:IGameObjectModel{
公共TModel模型;
公共游戏对象(TModel模型){
模型=模型;
}
公开射击(){
Model.Shoot();
}
:
}
Mi类