On graphs X comes first and is a point on the horizontal X axis, vice versa for Y. Why is this reversed in C#? The first dimension of an array is it’s height, which I guess could be construed as the amount of rows or horizontal lines of the array.
Just seems like X should be the width of the array and Y the height.
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/arrays/multidimensional-arrays
[Y,X,Z]
There is nothing about arrays (in C# and in general) defining the first dimension as the “height”.
semantics? Rows would be the equivalent to height.
Arrays are stored in what could be called row-major order in most implementations because it provides a number of performance benefits for the most typical operations.
On graphs X comes first
Does it? That’s just a convention. You don’t even really have to call the horizontal axis X at all.
Why is this reversed in C#?
C# arrays don’t have a notion of cartesian dimensions. You can use them for that, or you can use them for a million other reasons.
(Honestly, I rarely find uses for multidimensional arrays at all.)
Just seems like X should be the width of the array and Y the height.
There is no X, Y, width, or height in arrays.
It’s not “reversed in C#”. It’s not like the designers of C# designed arrays specifically for graphics. And this goes back to C, if you think about it. You’re making assumptions about what an array is, and what a multidimensional array is for.
Width and height, X and Y are all just a convention. They have nothing to do with arrays.
Arrays store data. They are stored in contiguous blocks of memory. Multidimensional arrays are also stored in contiguous blocks of memory. Memory is one dimensional, so how do we store 2 dimensions in 1 dimension?
Consider this array:
Is it up or down? left or right? It’s just an array 4 elements wide. How about we replace each element with an array?
It’s now an array of 4 elements containing arrays 3 elements wide.
In memory, these are really blocks of memory of the same size located next to each other. Why? Because pointer math makes it very simple to access the element you want. And how do we do that? By using the indexing the outer array first, then the inner array. we know that the outer array is made up of elements 3 wide. So when we access [2,1] pointer math calculates this as 2 * 3 + 1, as an offset from the first element.
C# doesn’t know and doesn’t care about your coordinate system, unfortunately. That’s what abstractions are for.
So now we have an array 4,3 outer to inner, and so it becomes Y, X
So multidimensional arrays are declared from outer rank to inner rank.
What will really bake your noodle is the actual layout of the data in memory is just numbers lined up contiguously. its just a 1 dimensional array, which is the most efficient way to do it. When you want to access x=1, y=2 C# just does a quick y*rowlength+x to get the index.
That math on access is cheaper than hopping around in memory.
Arrays aren’t meant only for representing geometric spaces, and the x, y, z
ordering is only one way to represent a space. You’ll also probably not like knowing that the signs of the Y axis in most graphics libraries are flipped: positive Y is down, not up.
The reason in both cases is the same: it’s how computer memory is laid out.
For arrays, think of the order as row, column
. For some reason I keep it straight in my head better if I use those words. It’s probably set up this way because the people who made early computers were generally using languages that read top-to-bottom and left-to-right. Think about it, do you believe it’s weird that we tend to talk about locations in a file by line number first, then character position? That’s also talking about Y coordinate first, then X coordinate. But saying “line 10, character 20” follows how we read text more naturally, so it feels more right. But I don’t think anglocentric languages are the only reason we index this way.
There are good hardware reasons to do it, too, especially when computers were much slower. “Dimensions” in arrays are a lie: computer memory is one straight line and every array is 1D. So how’s it work? Well, let’s say I have an int[3, 5]
, and I decide to call it three rows and five columns. I get my memory location x
, and I get room to put 15 int
values starting there. The first element is array[0, 0]
and is located at x
. The second element is array[0, 1]
and located at x + 1
. Once I’ve laid out the five elements for this row, ending at x + 4
, I consider the next element, x + 5
, to be array[1, 0]
.
Most C/C++ textbooks or CS courses end up showing a diagram that lays it out neat, and shows each row in a line in memory. It turns out any array[row, column]
access corresponds to a memory location at index row * rows + column
. If we wish, we can invert them, but then our memory layout is weird: it’s like we have “the first element of each row”, then “the second element of each row”, etc. Another user in this thread made a good text representation, have a look!
Anyway, this layout probably dates back to before there were even languages, and designers chose this layout because it’s generally fastest to work with memory in sequence due to how CPUs work. CRT monitors worked with horizontal scanlines, so it was best to choose a memory layout that looked like how the hardware worked so you could “race the beam”. We designed a lot of algorithms to work in row-column order either because of this hardware quirk or because everyone was already used to row-column order being fastest. If, for some reason, you have an algorithm that uses column iteration more, all you have to do is swap the indexes yourself.
(That’s also why positive Y is down on monitors. (0, 0)
is a good “start” for memory. A scanline is more or less a pixel. So video memory is laid out exactly how the beam moved and how we read: left-to-right, top-to-bottom. It’d be annoying to have to put a -
in front of every coordinate and invert the coordinates for memory indexing, so positive Y goes down. Believe it or not, a handful of graphics libraries use a different coordinate space!)
C# devs
null reference exceptions