108

Why is Array.Length an int, and not a uint. This bothers me (just a bit) because a length value can never be negative.

This also forced me to use an int for a length-property on my own class, because when you specify an int-value, this needs to be cast explicitly...

So the ultimate question is: is there any use for an unsigned int (uint)? Even Microsoft seems not to use them.

Ijas Ameenudeen
  • 9,069
  • 3
  • 41
  • 54
doekman
  • 18,750
  • 20
  • 65
  • 86
  • 3
    Despite the issues raised below, I think it should change to UInt. – alan2here Nov 28 '11 at 23:43
  • 3
    @alan2here making such a change would break almost all code out there so it wont happen if you ask me! – Peter Aug 23 '12 at 14:38
  • CLI compliance is enough reason all by itself, but even if it weren't, imagine a simple reverse `for` loop: `for (var i = arr.Length - 1; i >= 0; i--)`: If `Length` were unsigned, `i--` would change `i` from `0` to `uint.Max`, and this loop would either repeat infinitely or try to access an illegal part of the array. – StriplingWarrior Oct 06 '22 at 23:12

5 Answers5

68

Unsigned int isn't CLS compliant and would therefore restrict usage of the property to those languages that do implement a UInt.

See here:

Framework 1.1

Introduction to the .NET Framework Class Library

Framework 2.0

.NET Framework Class Library Overview

Kev
  • 118,037
  • 53
  • 300
  • 385
59

Many reasons:

  • uint is not CLS compliant, thus making a built in type (array) dependent on it would have been problematic
  • The runtime as originally designed prohibits any object on the heap occupying more than 2GB of memory. Since the maximum sized array that would less than or equal to this limit would be new byte[int.MaxValue] it would be puzzling to people to be able to generate positive but illegal array lengths.
  • Historically C# inherits much of its syntax and convention from C and C++. In those arrays are simply pointer arithmetic so negative array indexing was possible (though normally illegal and dangerous). Since much existing code assumes that the array index is signed this would have been a factor
  • On a related note the use of signed integers for array indexes in C/C++ means that interop with these languages and unmanaged functions would require the use of ints in those circumstances anyway, which may confuse due to the inconsistency.
  • The BinarySearch implementation (a very useful component of many algorithms) relies on being able to use the negative range of the int to indicate that the value was not found and the location at which such a value should be inserted to maintain sorting.
  • When operating on an array it is likely that you would want to take a negative offset of an existing index. If you used an offset which would take you past the start of the array using unit then the wrap around behaviour would make your index possibly legal (in that it is positive). With an int the result would be illegal (but safe since the runtime would guard against reading invalid memory)
Jannes
  • 1,784
  • 1
  • 17
  • 20
ShuggyCoUk
  • 36,004
  • 6
  • 77
  • 101
  • 4
    If nothing on the heap can be over 2Gb, then almost all arrays of length int.MaxValue are illegal since most types are larger than 1 byte. – ScottS Jan 29 '09 at 13:50
  • indeed but ((uint)(int.MaxValue)) + 1 would be guaranteed wrong for *anything*. int is itself far from perfect, but the balance of things makes it legit to stay with int as the type. – ShuggyCoUk Jan 29 '09 at 17:09
  • 2
    Starting out with be an explict ArrayIndex type (essentially size_t) that would translate cleanly and safely to an int as needed perhaps would make it easier in future to make really use allowing for > 2GB arrays in future with less pain. But pragmatics say java has same problem so why take the risk – ShuggyCoUk Jan 29 '09 at 17:13
  • This is very informative. In Windows Communication Foundation as of .NET 4.0 - the largest values were 2,147,483,647 for `maxArrayLength` and `maxBytesPerRead` which retrospectively makes a lot of sense with this information. Connecting the dots... – Derek W Feb 01 '14 at 17:29
  • Just want to add that C# pointer arithmetic in unsafe contexts also allows negative indexes. See the example here: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/stackalloc – Mike Marynowski Oct 16 '17 at 02:43
  • So how do you get the length of your array/index it in .NET 4.5 if it can have as many as `uint.MaxValue` objects? – Adam Dec 01 '18 at 17:17
  • @Adam: There's a [`LongLength` property](https://learn.microsoft.com/en-us/dotnet/api/system.array.longlength?view=netframework-4.5) for that. But that can only happen when the application is specifically configured to allow it, and the length of any single dimension [is still limited](https://learn.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element?redirectedfrom=MSDN#remarks). – StriplingWarrior Oct 06 '22 at 23:10
1

Looks like nobody provided answer to "the ultimate question".

I believe primary use of unsigned ints is to provide easier interfacing with external systems (P/Invoke and the like) and to cover needs of various languages being ported to .NET.

Constantin
  • 27,478
  • 10
  • 60
  • 79
  • Unsigned types are essential when concatenating multiple smaller values to produce a larger one. One can combine two UInt16's to make a UInt32 by computing `(HighPart << 16) + LowPart`, and one may split a UInt32 into two UInt16's via `(Uint16)(Value >> 16)` and `(Uint16)(Value & 65535)`. Such operations would be very inconvenient if `LowPart` had to be a signed type. That having been said, interactions between signed and unsigned types are often confusing and problematical. Unsigned types should in many ways be considered a world of their own. – supercat Feb 03 '13 at 17:45
1

I think it also might have to do with simplifying things on a lower level, since Array.Length will of course be added to a negative number at some point, if Array.Length were unsigned, and added to a negative int (two's complement), there could be messy results.

-2

Typically, integer values are signed, unless you explicitly need an unsigned value. It's just the way they are used. I may not agree with that choice, but that's just the way it is.

For the time being, with todays typical memory constraints, if your array or similar data structure needs an UInt32 length, you should consider other data structures.

With an array of bytes, Int32 will give you 2GB of values

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • 16
    "but that's just the way it is." -- no, things are never just the way they are. There's always a design decision being made, and it always pays to ask why. One might learn something from the pros and cons, or engage the designer (in some cases) in a discussion about the them. Always ask questions! :) – Jonas Kölker Jun 25 '09 at 10:54