3

所以我在 C# 中迈出了第一步,并且正在制作一个简单的拼图。当我对瓦片的位置进行建模时,我想要具有值语义。所以,据我所知,基本上有两种方法可以做到这一点,使用结构或使用元组。

在元组的情况下,我的代码如下所示:

public class TilePosition : Tuple<int,int>
{
    public int HComponent{get { return Item1; }}
    public int VComponent{get { return Item2; }}

   public TilePosition(int horizontalPosition, int verticalPosition)
        : base(horizontalPosition, verticalPosition)
    {
    }
}

结构解决方案如下所示:

public struct TilePosition 
 {
    private readonly int hComponent;
    private readonly int vComponent;
    public int HComponent { get { return hComponent; } }
    public int VComponent { get { return vComponent; } }

   public TilePosition(int hComponent, int vComponent)
    {
        this.hComponent = hComponent;
        this.vComponent = vComponent;
    }

   public static bool operator ==(TilePosition position1, TilePosition position2)
    {
        return position1.Equals(position2);
    }

   public static bool operator !=(TilePosition position1, TilePosition position2)
    {
        return !(position1 == position2);
    }
}

元组更简洁,但它公开Item1并且Item2在公共 API 中会造成混淆,即使我在它们周围添加了 H 和 V 组件属性。

该结构需要更多代码,并且我收到一个编译器警告,提示我应该如何覆盖 Equals 和 GetHashCode 因为我正在覆盖==and !=,但是如果我这样做了,我不会从使用结构中得到任何东西(从语义和句法的角度来看) ) 因为它与传统类的代码完全相同。

那么除了没有 Item 属性的噪音之外,在子类 Tuple 上使用 struc 有什么好处吗?

我的两个解决方案的行为方式是否与我预期的相同,或者是否有我应该注意的细微差别?

4

3 回答 3

8

(顺便说一句,IEquatable<TilePosition>在这两种情况下实现也是很好的——尤其是在 struct 情况下,以避免装箱。)

那么除了没有 Item 属性的噪音之外,在子类 Tuple 上使用 struc 有什么好处吗?

鉴于它是不可变的,在这两种情况下,您在这两种情况下都大致具有“值语义”,但仍然存在差异......

  • 类类型的实例需要堆上的空间(假设 CLR 没有进行转义检测等);结构类型的值在某些情况下可能只使用堆栈
  • 传递类类型的值仅意味着传递引用(4 字节或 8 字节,具体取决于 CLR 架构);传递结构类型的值确实传递了值(所以 8 个字节)
  • 在类类型版本null中是一个有效值;在您需要用来TilePosition?指示可能不存在的值的结构类型版本中
  • 在 struct 版本new TilePosition()中是有效的,并且两个字段的值都为 0(这将是默认值,例如字段和数组元素)
  • 由于您还没有密封您的课程,因此有人可以创建一个可变的子类;因此,客户认为它是完全不可变的是不安全的(你可能应该把它封起来……)
  • 您可以将您的类类型与任何使用 的代码一起使用Tuple<,>,而 struct 类型显然不是这种情况
  • will的含义==在两种类型之间有所不同。即使您==在类类型中重载,调用者最终仍可能只是比较引用。在 struct 的情况下,您仍然可能最终会比较盒装引用,但无济于事。

当然,这些只是差异- 它们是否算作一种方法或另一种方法的好处取决于您的要求。

于 2013-01-24T13:14:03.510 回答
0

如何使用锯齿状数组并在每个字段上保存一个项目。这将更紧密地遵循瓷砖拼图:

  • 瓷砖和它们的空间是 1:1 映射。所以每个图块/空间只能有一个空间/图块。
  • 无需比较瓷砖组件。
  • 移动瓷砖很容易,例如

        if (fields[x, y] = null)
        {
            fields[x, y] = fields[oldX, oldY];
            fields[oldX, oldY] = null;
        }
    
于 2013-01-24T13:23:28.027 回答
0

你最好的选择是正确地做它并投入所有的工作。如果你想要一个结构而不是一个类(这可能适合你),这里有一个示例实现:

public struct TilePosition: IEquatable<TilePosition>
{
    public TilePosition(int horizontalPosition, int verticalPosition)
    {
        _x = horizontalPosition;
        _y = verticalPosition;
    }

    public int HComponent
    {
        get
        {
            return _x;
        }
    }

    public int VComponent
    {
        get
        {
            return _y;
        }
    }

    public static bool operator == (TilePosition lhs, TilePosition rhs)
    {
        return lhs.Equals(rhs);
    }

    public static bool operator != (TilePosition lhs, TilePosition rhs)
    {
        return !lhs.Equals(rhs);
    }

    public bool Equals(TilePosition other)
    {
        return (_x == other._x) && (_y == other._y);
    }

    public override bool Equals(object obj)
    {
        return obj is TilePosition && Equals((TilePosition)obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return (_x*397) ^ _y;
        }
    }

    private readonly int _x;
    private readonly int _y;
}
于 2013-01-24T13:28:15.060 回答