【问题标题】:lost in inheritance and polymorphism迷失在继承和多态中
【发布时间】:2016-01-31 21:16:18
【问题描述】:

我需要如何重构或重构我的代码,以便我可以使用嵌入和继承的对象以最少的代码重复来按预期工作。 这段代码只是一个例子,我认为这是我的 OOP 思维中的一个基本错误......

该示例与 Car、Engine 和 Driver 等书籍中几乎所有常见的基类相同: 当我添加从基类继承的特殊类(如 Racecar:Car 和 TurboEngine:Engine)并将属性添加到特定类(如对 TurboEngine 的 boost)时,当我想使用与基类一样多的代码和方法时,我会遇到同样的问题类而不覆盖几乎所有方法。

我已经将我的字段重构为属性并尝试使用newoverride,但逻辑问题保持不变。

如果该代码太多,我可以尝试将其缩短为我遇到的单个问题。

示例代码显示了问题。上/下铸造无济于事。我想这更像是一个基本的思维错误,因为 OOP 编程对于一个老汇编程序来说很难学习。

编辑----更新:

好的,这是设计使然。 https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science) https://en.wikipedia.org/wiki/Liskov_substitution_principle

我现在感觉有点松懈……卡在那里想:不可能是真的。 应该有更多地方可以指出、收集和展示这些常见错误,具体取决于开发人员的经验水平。同样在许多书中,它也远没有被强调。现在知道了它的正确术语,我发现了更多。但是还是有很多深入的技术讲解不适合新手。这样可以节省很多时间:)

var root = new Vector2D(0,0);

GameField2D fGame = new GameField2D(10,10);
fGame.Init();
fGame.Field[1,1].ParentPoint = root;

fGame.Field[1,1].Color = 1; //<--ERROR to be expected
var test = (TetrisField2D)fGame; //<---ERROR cast not possible

TetrisField2D fTetris = new TetrisField2D(10,10);
fTetris.Init();
fTetris.Field[1,1].ParentPoint = root; //<--ERROR Nullreference because only base class field is initiated.

以下是课程:

继承者:

public class TetrisField2D: GameField2D {
    public int Lifes;
    public TetrisPoint[,] Field; //I want of course Tetrispoints with color

    public TetrisField2D(): base(){}
    public TetrisField2D(int x,int y) : base (x,y) {}
}

基类

public class GameField2D { //"generic"
    public GamePoint[,] Field;
    private int size;

    //CTORs
    public GameField2D(){
      Field = new GamePoint[9,9];
      size = 10;
    }
    public GameField2D(int x,int y){
      Field = new GamePoint[x-1,y-1];
      size= x-1;
    }
    public GameField2D(GamePoint[,] f){
      Field = f;
      size = f.GetLength(0);
    }
    public void Init() {
      for (int i = 0; i < size; i++) {
        for (int j = 0; j < size; j++) {
            Field[i,j] = new GamePoint();
        }
      }
    }
}

包含点类:

public class TetrisPoint : GamePoint{
  public int Color;

  public TetrisPoint(){}
  public TetrisPoint(int x, int y, int col) : base(x,y) {
    Color = col;
  }
}
public class GamePoint   { 
  public Vector2D ParentPoint;
  public bool Appears;
  public int IsUsed;

  public GamePoint(){   }   
  public GamePoint(int x, int y, bool a=false) {
    this.ParentPoint = new Vector2D(x,y); 
    Appears = a;
   }
}

public struct Vector2D {
   public int X,Y;
   public Vector2D(int x,int y) {
        this.X=x;
        this.Y=y;
   }
}

【问题讨论】:

    标签: c# inheritance casting polymorphism


    【解决方案1】:

    该示例与 CarEngine 等书籍中的几乎所有常见基类相同...像 boost to TurboEngine 这样的专门类当我想使用与基类中的代码和方法一样多的代码和方法而不覆盖几乎所有方法时,我会遇到同样的问题。

    您遇到的问题是面向对象设计的经典问题。 OO 可以很好地模拟派生类更具体不引入限制的情况。赛车比汽车更具体,但“赛车只有涡轮发动机”是对可以放入汽车的发动机种类的限制。

    没有明显的灌篮方法可以解决这个问题。我在我的博客上写了一系列文章,更详细地描述了这个问题以及人们试图解决它的一些方法;你可能会觉得这很有趣。

    http://ericlippert.com/2015/04/27/wizards-and-warriors-part-one/

    【讨论】:

    • 我现在感觉有点失落了……卡在那里想:不可能是真的。应该有更多的地方来收集和展示这些常见错误取决于一些开发人员的水平。这可能需要很多时间:)
    【解决方案2】:

    您不能从基类转换为派生类,只能反过来。您必须创建一个俄罗斯方块而不是基础 GameField。

    GameField2D fGame = new TetrisField2D(10,10);
    

    不幸的是C# does not support property covariance(我真的希望它发生在我之前。),所以要解决拥有不同类型字段的问题,您需要在您的俄罗斯方块字段中使用new 关键字班级。但是,只有在转换为俄罗斯方块时才修改字段非常重要,否则您实际上将修改基类的字段。

    【讨论】:

    • 我知道,但是您的示例不起作用,因为 GameField2D 不包含 .color....还需要编辑:该术语似乎 100% 适合我的问题,这很难谷歌 返回类型协方差是您用返回更特定类型的基类方法覆盖返回不太特定类型的基类方法
    • @FalcoAlexander 看看我对协方差的看法。我只想编写您的示例代码而不进行强制转换,创建一个 TetrisField2D 并将其分配给一个 TetrisField2D 并且不要尝试强制转换为基类,没有理由这样做。
    • 现在知道了!是否有一些最佳实践或 c# 模式或解决方法可以让我指出?
    • somewhat of a workaround using virtual methods,但是我在使用这个模式的时候一直没有找到解决这个问题的好办法。
    • 我也应该说有一个流行的提案C# 7 to include covariant return types.
    猜你喜欢
    • 2013-04-22
    • 2011-12-19
    • 1970-01-01
    • 1970-01-01
    • 2018-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多