所以我在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) { } }
struct解决方案如下所示:
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,因为我正在覆盖==
和!=
,但如果我这样做,我什么都没得到从使用结构(从语义和句法的角度来看),因为它与传统类完全相同。
除了没有Item属性的噪声之外,在子类化元组上使用struc还有什么好处吗?
我的解决方案是否都会以我预期的方式运行,或者我应该注意哪些细微差别?
答案 0 :(得分:8)
(顺便说一句,在两种情况下都可以实现IEquatable<TilePosition>
- 特别是在结构案例中,以避免装箱。)
除了没有Item属性的噪声之外,在子类化元组上使用struc还有什么好处吗?
鉴于它是不可变的,在这两种情况下你都有大致“价值语义”,但仍有差异...
null
是一个有效值;在结构类型版本中,您需要使用TilePosition?
来指示可能缺少的值new TilePosition()
中有效且两个字段的值都为0(这将是默认值,例如字段和数组元素)Tuple<,>
的代码一起使用,而结构类型显然不是这种情况==
的含义在两种类型之间会有所不同。即使您在类类型中重载==
,调用者仍然可能只是比较引用。在结构案例中,您仍然可能最终无法比较盒装引用。这些只是差异当然 - 对于一种方法,它们是否算作好处,另一种取决于您的要求。
答案 1 :(得分:0)
如何使用锯齿状数组并在每个字段上保存项目。这将更紧密地遵循拼图:
移动瓷砖很简单,例如
if (fields[x, y] = null)
{
fields[x, y] = fields[oldX, oldY];
fields[oldX, oldY] = null;
}
答案 2 :(得分: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;
}