All such problems are solved in a really simple way, but there are some simple but not so obvious techniques.
Here is the wrapper class which behaves like a 2D and 1D array at the same time, based on the same physical locations of array elements. The under-the-hood
implementation array is the 1D one:
interface IArray2D<T> {
T this[int x, int y] { get; set; }
}
public class DualUseArray<T> : IArray2D<T> {
public DualUseArray(int sizeX, int sizeY ) {
this.implementation = new T[sizeX * sizeY];
this.width = sizeX;
}
public T this[int x, int y] {
get { return implementation[GetIndex(x, y)]; }
set { implementation[GetIndex(x, y)] = value; }
}
int GetIndex(int x, int y) {
return y * width + x;
}
public T this[int index] {
get { return implementation[index]; }
set { implementation[index] = value; }
}
T[] implementation;
int width;
}
Here is the double-use way:
DualUseArray<string> myArray = new DualUseArray<string>(12, 100);
myArray[120] = "120";
myArray[12, 10] = "12, 10";
string value = myArray[120];
Got the idea?
Pay attention for the auxiliary interface used. It looks like this is the only way to introduce more than one indexed property "
this
" in the same type. Of course all indexed properties should be different in their signatures.
You can improve this solution in some ways: you can check up the range constraints for the 2D indices, as well as check up validity of constructor parameters, you can add constructors initializing the array elements to same value or some other ways, maybe something else. In the same way, you can introduce representations like 3D, 4D, etc...
—SA