I created a struct in C# for storing the X, Y and Z coordinates for characters, objects and rooms within my current PBBG project Perenthia. The struct, called Vector, is serializable and can be used as the key value in a sorted or generic dictionary. Here is the code for the class.

[Serializable]
public struct Vector : IEquatable<Vector>
{
public static readonly Vector Empty = new Vector(0, 0, 0);
public Vector(int x, int y, int z)
{
_x = x;
_y = y;
_z = z;
}
public void SetLocation(int x, int y)
{
this.SetLocation(x, y, this.Z);
}
public void SetLocation(int x, int y, int z)
{
_x = x;
_y = y;
_z = z;
}
public Vector Copy()
{
return new Vector(this.X, this.Y, this.Z);
}
public static Vector FromString(string value)
{
if (!String.IsNullOrEmpty(value))
{
string[] parts = value.Split(',');
if (parts != null && parts.Length == 3)
{
int x, y, z;
if (Int32.TryParse(parts[0], out x))
{
if (Int32.TryParse(parts[1], out y))
{
if (Int32.TryParse(parts[2], out z))
{
return new Vector(x, y, z);
}
}
}
}
}
return Vector.Empty;
}
#region GetHashCode
public override int GetHashCode()
{
// The Y value should always come first in any kind of sorting, comparison or hashing operations
// followed by X and then Z because typical loops would start with the Y value.
return (this.Y.GetHashCode() + this.X.GetHashCode() + this.Z.GetHashCode());
}
#endregion
#region Equals
public bool Equals(Vector obj)
{
// The Y value should always come first in any kind of sorting, comparison or hashing operations
// followed by X and then Z because typical loops would start with the Y value.
if (obj != null)
{
if (obj.Y == this.Y)
{
if (obj.X == this.X)
{
return (obj.Z == this.Z);
}
}
}
return false;
}
public override bool Equals(object obj)
{
if (obj is Vector)
{
return this.Equals((Vector)obj);
}
return base.Equals(obj);
}
#endregion
#region ToString
public override string ToString()
{
return String.Format("{0},{1},{2}", _x, _y, _z);
}
public string ToString(bool forDisplay)
{
if (forDisplay)
{
return String.Format("X = {0}, Y = {1}, Z = {2}", _x, _y, _z);
}
return this.ToString();
}
#endregion
#region Operators
public static Vector operator +(Vector v1, Vector v2)
{
return new Vector(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}
public static Vector operator -(Vector v1, Vector v2)
{
return new Vector(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
}
public static bool operator ==(Vector v1, Vector v2)
{
return v1.Equals(v2);
}
public static bool operator !=(Vector v1, Vector v2)
{
return (!v1.Equals(v2));
}
public static bool operator >=(Vector v1, Vector v2)
{
// The Y value should always come first in any kind of sorting, comparison or hashing operations
// followed by X and then Z because typical loops would start with the Y value.
if (v1.Y >= v2.Y)
{
if (v1.X >= v2.X)
{
return (v1.Z >= v2.Z);
}
}
return false;
}
public static bool operator <=(Vector v1, Vector v2)
{
// The Y value should always come first in any kind of sorting, comparison or hashing operations
// followed by X and then Z because typical loops would start with the Y value.
if (v1.Y <= v2.Y)
{
if (v1.X <= v2.X)
{
return (v1.Z <= v2.Z);
}
}
return false;
}
#endregion
#region Properties
private int _x;
public int X
{
get { return _x; }
set { _x = value; }
}
private int _y;
public int Y
{
get { return _y; }
set { _y = value; }
}
private int _z;
public int Z
{
get { return _z; }
set { _z = value; }
}
#endregion
#region IEquatable<Vector> Members
bool IEquatable<Vector>.Equals(Vector other)
{
return this.Equals(other);
}
#endregion
}