C#成员变量继承

6

我很新于C#,但我有相当丰富的编程背景。

我的目标是:为游戏定义不同的MapTiles。我已经定义了基本的MapTile类如下:

public class MapTile
{
    public Texture2D texture;
    public Rectangle mapRectangle;

    public MapTile(Rectangle rectangle)
    {
        this.mapRectangle = rectangle;
    }
}

I then define a subclass GrassTile like this:

class GrassTile : MapTile
{
    new Texture2D texture = Main.GrassTileTexture;
    new public Rectangle mapRectangle;

    public GrassTile(Rectangle rectangle) : base(rectangle)
    {
        this.mapRectangle = rectangle;
    }
}

在我的Main类中,我像这样创建了一个新的地图瓦片:
Maptile testTile;
testTile = new GrassTile(new Rectangle(0, 0, 50, 50);

然而,当我尝试渲染这个testTile时,它的纹理最终变成了null。如果我在MapTile内定义纹理,则我的代码运行良好,因此与先前的实现无关。
那么,如何让GrassTile能够修改MapTile的成员变量texture?或者让我的主类识别GrassTile的纹理而不是MapTile的纹理?我也试过使用接口,但我无法声明接口成员变量。C#继承中是否还有我不了解的内容?
提前感谢。

你能澄清一下对Main.GrassTileTexture的引用吗?那是一个静态属性吗? - jake
抱歉,这是一个静态属性,当分配给MapTile时可以正常工作,但分配给GrassTile时却不能。 - Logicon211
5个回答

8

子类会继承父类的成员,您不需要指定它们。此外,最好使用属性而不是公共字段。

public class MapTile
{
    public Texture2D Texture { get; set; }
    public Rectangle MapRectangle { get; set; }

    public MapTile(Rectangle rectangle)
    {
        MapRectangle = rectangle;
    }
}

public class GrassTile : MapTile
{    
    public GrassTile(Rectangle rectangle) : base(rectangle)
    {
        Texture = Main.GrassTileTexture;
    }
}

谢谢,解决了我的问题。不过,我有一个小问题,在C#中不能定义property,我尝试过“property”和“public property”,但没有显示任何东西。另外,那些{ get; set; }块不是必需的,它们是干什么用的? - Logicon211
1
@Logicon211 属性在 C# 中有特定的语法,您可以在 msdn about properties 上阅读。此外,{ get; set; } 块是 auto-implemented property definition 的一部分。我认为阅读关于 public fields 和 properties 之间的区别 对您非常有用。抱歉没有及时回复 - 我不在计算机旁。 - Sergey Berezovskiy

2
您有一点语法混淆。 "new" 关键字既可以用作实例化操作符,就像您在主程序中所做的那样,也可以用作成员修饰符以隐藏继承的成员,这就是您在 GrassTile 中所做的事情。您实质上是在重新定义成员。正确的版本可能是:
   class GrassTile : MapTile
    {
        public GrassTile(Rectangle rectangle) : base(rectangle)
        {
            texture = Main.GrassTileTexture;
        }
    }

由于您的矩形在基础构造函数中已经设置,因此无需再次设置。


另外一件事是,你不应该像那样使用静态变量。C# 被认为是纯面向对象的,我的面向对象感觉告诉我,即使你绝对必须使用静态变量,也应该在它逻辑上属于的类中使用。在这种情况下,我会说它应该是 MapTile.GrassTexture 或 GrassTile.Texture。 - canahari

0

继承使得继承的字段在派生类中持久存在。

public sealed class GrassTile : MapTile
{
    public GrassTile(Rectangle rectangle) : base(rectangle)
    {
        texture = Main.GrassTileTexture;
    }
}

0

我认为在这里最好澄清一下你如何使用new关键字。 new可以用两种方式。作为运算符和修饰符。

new "运算符"

新的运算符用于创建对象和调用构造函数,例如:

Class1 MyClass  = new Class1();

new 修饰符

使用 new 修饰符可以显式地隐藏从基类继承的成员。要隐藏继承的成员,请在派生类中使用相同的名称声明它,并使用 new 修饰符进行修改。

考虑以下类:

public class MyBaseC 
{
   public int x;
   public void Invoke() {}
}

在派生类中声明一个名为Invoke的成员将隐藏基类中的Invoke方法,即:
public class MyDerivedC : MyBaseC
{
   new public void Invoke() {}
}

然而,字段x不会受到影响,因为它没有被类似名称隐藏。

通过继承进行名称隐藏有以下几种形式:

  • 在类或结构中引入的常量、字段、属性或类型将隐藏所有具有相同名称的基类成员。

  • 在类或结构中引入的方法将隐藏基类中具有相同名称的属性、字段和类型。它还将隐藏所有具有相同签名的基类方法。

  • 在类或结构中引入的索引器将隐藏所有具有相同签名的基类索引器。

由于您正在进行名称隐藏并声明一个Maptile类型,但初始化为子GrassTile类型和父MapTile类型从未初始化您的Texture成员,这就是为什么您看到了null的原因。


0

在你的代码中有几个需要改进的地方:

首先,属性Texture已经在基类MapTile中定义,因此不需要在类GrassTile中重新定义;派生类(GrassTile)从基类(MapTile)继承了这个成员。

接下来,你可以考虑修改基类'MapTile'为抽象类,因为我认为它本身没有直接的行为(纹理必须由具体的派生类实现提供)。

然后,你可以将texture的声明修改为受保护的,因为它不应该以这种方式在类层次结构之外被访问。或者,将其转换为带有getset访问器的属性。

最后,你使用 new 修饰符的方式是不正确的。在这种情况下,该修饰符仅旨在允许您使用新行为覆盖某些行为的基本实现(而不是继承基本行为)。在这种情况下,你正在声明一个“新”的纹理在 GrassTile 中,这将覆盖基本实例,但仅在通过类 GrassTile 引用时。

{
    MapTile m1 = new GrassTile(...);
    //m1.texture == null;
    GrassTile m2 = new GrassTile(...);
    //m2.texture = Main.GrassTileTexture
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接