6

在那里,我正在用 C# XNA 4.0 制作一个 2D 游戏,但又遇到了我的一个小烦恼;矩形。_ 对于那些使用基本碰撞的人来说,这几乎是必须的。对于几乎所有创建的游戏对象,您都需要一个矩形。然后我去改变 X,检查碰撞,或其他任何东西。在那里我开始了永无止境的战斗objectName.Rectangle.Whatever。为了解决这个问题,我当然提供了 objectName 类属性/方法来为我访问这些属性/方法。

那我就敢做梦了。我有宏伟的设计来制作一个基本的游戏对象类,所有可绘制的东西都将从中继承,这将允许父子关系、精确的局部坐标(浮点数),并保存纹理/精灵批次。为了完成这个,我准备从 Rectangle 继承,以使用它拥有的所有方法和属性。哎呀,每当需要一个 Rectangle 时,我都可以懒得说 objectName,而不是 objectName.Rectangle。

然后我意识到,没有机会。我一开始很沮丧,因为我非常聪明的想法被粉碎了。从那时起,我完美的小类拥有一个矩形,可以根据需要使用各种方法和属性访问它。我也借此机会让它继承自 XNA DrawableGameComponent。虽然从长远来看,这更实用,但每次我查看 draw 或 update 方法并看到对 rectangle 的调用时,我经常想知道,有没有希望做我想做的事?有没有一些我可以做的聪明的工作?或者是从一个 Rectangle 继承而来的,真的是我无法掌握的?

虽然使用 XNA 中提供的 DrawableGameComponent 类允许大多数与游戏对象相关的动作发生在类 Update() 方法中,但每次在类之外我需要引用的不是 Rectangle 的属性,而是 Rectangle 本身,我有点考虑到我的对象实际上是增强型矩形,这让我很生气。然后我又一次发现自己在问:

有没有办法从预定义的结构继承,或者给项目留下你的印象(一种变通方法)?

4

3 回答 3

16

不继承,但您可以使用扩展方法向 Rectangle 对象添加许多“默认”功能。例如

  //(inside a static class)
  public static int GetSurface(this Rectangle rect){return rect.Width * rect.Height;}

  //calling
  Rectangle rect;
  var s = rect.GetSurface();

也就是说,我通常做的是封装所述结构。使用该类作为基础对象,并添加一个运算符,以便可以将其隐式转换为 Rectangle。这样它就可以传递给一个需要矩形而不进行强制转换的方法。

    public class MyRect //class so you can inherit from it, but you could make your own struct as well
    {
        public int X { get; set; }
        public int Y { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
        public int Right { get { return X + Width; } }
        public int Bottom{ get { return Y + Height; } }

        public static implicit operator Rectangle(MyRect rect)
        {
            return new Rectangle(rect.X, rect.Y, rect.Width, rect.Height);
        }

        public static implicit operator MyRect(Rectangle rect)
        {
            return new MyRect { X = rect.X, Y = rect.Y, Width = rect.Width, Height = rect.Height };
        }

    }       
 }

现在您可以手动或从现有的矩形创建自己的矩形:

  MyRect rect = ARectangleVar

您可以在期望 Rectangle 而不进行强制转换的遗留方法中使用它

于 2012-06-16T17:56:41.420 回答
4

这是一种可能的解决方法(将其放入MyClass,您所说的具有 Rectangle 属性的类):

public static implicit operator Rectangle(MyClass obj)
{
    return obj.Rectangle;
}

例如,这将允许您将对象传递给期望 a 的方法Rectangle,并且它可以工作。请注意,与真正的继承不同,存在的 Rectangle 与MyClass您传入的实例不同,它只是一个由值组成的结构。

于 2012-06-16T17:55:01.870 回答
0

值类型从根本上与适用于类类型的继承风格不兼容,但能够以编译器识别派生之间的双向身份保持转换的方式声明“派生结构”将是有帮助的类型和原件。派生的结构需要有一个字段,称为“Base”(尽管 C# 编译器可以接受关键字base作为替代),其类型是被派生的结构的类型。名称与周围类型不匹配的“Base”的所有成员都将被视为封闭结构的成员。

“派生结构”将提供但目前缺乏的主要内容是:

  1. 无需添加额外的语法间接层即可访问包装结构的成员的能力。例如,如果有一个从 `Point` 定义的类型的实例 `MyFancyPoint`,则可以说 `MyFancyPoint.X += 5` 而不是 `MyFancyPoint.Base.X += 5`。
  2. 一次进行多个隐式或显式类型转换的能力。C# 通常只允许从 `X` 到 `Y` 的转换,除非存在直接转换,部分原因是搜索所有可能的转换序列很困难,部分原因是这样做通常会产生关于哪个转换序列的歧义应该被雇用。如果假设包装特定结构的所有类型都表示具有相互保持身份的转换的“云”,则两个派生结构 XX:X 和 YY:Y 之间的转换可以明确地视为到 X 的转换,然后是 `Y`,然后是 `YY`(如果存在 `X` 到 `Y` 的转换,这将是合法的)。
  3. 将 ByRef 对一种类型的实例视为对另一种类型的实例的 ByRef 以及对一种类型的字段的 ByRef 被视为另一种中相应字段的 ByRef 的能力。

我不确定是否需要对运行时进行任何更改才能支持这些功能中的任何一个,如果人们接受盒装派生类型的项目只能作为相同的派生类型拆箱;允许将装箱版本的结构直接转换为可转换身份的派生实例的实例会有所帮助,但可能会增加复杂性。我不确定是否需要这种行为才能使泛型工作。不幸的是,我感觉许多负责做出此类决定的人真的不喜欢值类型。由于 .net 历史早期的一些设计决策(尤其是缺少“const ref”参数和一种通过ref),使结构以语义正确的方式工作可能是一个挑战,我认为一些实施者宁愿以结构的问题为借口不改进 .net 对它们的处理,而不是添加可以解决问题的功能。

于 2012-06-17T17:11:59.533 回答