Reputation: 330
So I'm making my first steps in C# and was making a simple tile puzzle. When I was modeling the position of a tile I wanted to have value semantics. So, as far as I can see there are basically two ways of doing this, with a struct or with a Tuple.
In the case of the Tuple my code looks like this:
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) { } }
The struct solution would look like this:
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); } }
The tuple is conciser but it exposes Item1
and Item2
which would be confusing in a public API, even though I have added the H and V component properties around them.
The struct need more code and I get a compiler warning about how I should override Equals and GetHashCode because I'm overriding ==
and !=
, but if I do that I'm not getting anything from using a struct (from the semantic and syntactic point of view) because it wold be exactly the same code with a conventional class.
So are there any benefits from using a struc over a subclassed Tuple aside from not having the noise of the Item properties?
Would both of my solution behave in the same way as I expect or are there nuances I should be aware of?
Upvotes: 3
Views: 3193
Reputation: 109567
Your best bet is to do it properly and put in all the work. If you want a struct rather than a class (which may be appropriate for you), here's a sample implementation:
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;
}
Upvotes: 0
Reputation: 1500515
(As an aside, it would be good to implement IEquatable<TilePosition>
in both cases too - particularly in the struct case, to avoid boxing.)
So are there any benefits from using a struc over a subclassed Tuple aside from not having the noise of the Item properties?
Given that it's immutable, in both cases you have roughly "value semantics" in both cases, but there are still differences...
null
is a valid value; in the struct type version you'd need to use TilePosition?
to indicate a possibly-absent valuenew TilePosition()
is valid and will have values of 0 for both fields (and this will be the default value, e.g. for fields and array elements)Tuple<,>
, whereas that's clearly not the case for the struct type==
will differ between the two types. Even if you overload ==
in the class type, a caller could still end up just comparing references. And in the struct case, you could still end up comparing boxed references, unhelpfully.These are just differences of course - whether they count as benefits for one approach or the other depends on your requirements.
Upvotes: 8
Reputation: 5116
How about using a jagged array and holding an item on each field. This would more closely follow the tile puzzle:
Moving tiles is easy, example
if (fields[x, y] = null)
{
fields[x, y] = fields[oldX, oldY];
fields[oldX, oldY] = null;
}
Upvotes: 0