7

I am new to C#, I have some background in C++ and C and this is why I fail to grasp, why Lists<> are indexed by int and not some unsigned integer like uint. This is highly illogical, because there cannot be an element i.e. -1 or so.

This came up when the following code failed to run:

List<string> some_list = new List<string>();
/* Insert some strings */

for (uint i = 0; i < some_list.Count; ++i) {
    string element = some_lists[i]; // Error: No possible conversion from uint to int
    /* do something with string_element */
}

In C++ this would give me a compiler warning, but still compile fine. So what is C#'s reasoning for using signed integers as indices in, say, Lists? This does not look very sharp to me, but maybe someone can enlighten me.

nada
  • 2,109
  • 2
  • 16
  • 23
  • 1
    CLS compliance. – CodeCaster Feb 13 '19 at 15:15
  • 2
    @CodeCaster - ok, why does the CLR use signed int? – H H Feb 13 '19 at 15:16
  • 3
    This is a good question but I wouldn't be surprised if the answer is simply, "because someone at some-point decided that's how it should work" ¯\_(ツ)_/¯ – Liam Feb 13 '19 at 15:17
  • 1
    Lists usually aren't used for index work, their advantage is the easier handling when searching and manipulating. Their counter part is an array. – Hille Feb 13 '19 at 15:18
  • 1
    And if you're looking for some index work you should switch to a Dictionary. – Hille Feb 13 '19 at 15:19
  • 3
    The non-negative range of a `int` is 0 to over two billion. The range of a `uint` is 0 to over 4 billion. Yes, it's twice as big a range, but, who cares; no one creates lists with over two billion entries. You could argue that accepting negatives is a design flaw, but, we have `ArgumentOutOfRangeException` for that. The advantage of using an `int` is that most calculations are done using `int`s, not `uint`s. I don't believe there's an implicit conversion between either type, so if a `uint` was used, it would have to be used everywhere. `int` here rolls off my fingers much better – Flydog57 Feb 13 '19 at 15:27
  • 2
    To expand upon Flydog's point: Suppose you have two lists. Suppose one of them has one more item than the other. Suppose you would like to know which one is the larger. Suppose you subtract the size of the first from the size of the second. Suppose the size is an int. The result of the subtraction is either 1 or -1. **Exercise: What are the results if the size is a uint?** Now do you see why it only makes sense to make sizes ints? – Eric Lippert Feb 13 '19 at 16:22
  • @HenkHolterman: CodeCaster is not talking about the CLR, which uses ints or uints interchangeably in the VES. They're talking about the CLS, the Common Language Subset, which describes the data types that a CLS-compliant API is permitted to use. The CLS is designed to lower the burden on language providers; not all languages are required to understand the uint type, because it is pretty useless. – Eric Lippert Feb 13 '19 at 16:26
  • @EricLippert To check which list is larger, one would compare their size by > or something. In the rare (?) case a difference of two list sizes is calculated, casting them to int would be no problem. – nada Feb 13 '19 at 16:36
  • Yes, I kind of robo-typed that R. – H H Feb 13 '19 at 16:36
  • @EricLippert Oh and to answer your exercise - in C# i guess the result would be either 1 or uint.MaxValue – nada Feb 13 '19 at 16:40
  • @nada: casting to int is no problem, except when you forget to do it and your program is wrong. C# is designed so that errors which are common in other languages become difficult or impossible. The point is: almost all arithmetic eventually involves negative numbers, so all numbers should have negatives in their range. The argument to have is not "why isn't this quantity that is logically non-negative an unsigned type?" The better argument is "why are sizes and indices not represented by their own wrapper type that does the right thing?" – Eric Lippert Feb 13 '19 at 16:40
  • Because that is the actually relevant deficiency when comparing the C# type system to the C type system: there should be a "size_t" equivalent that means "I'm the size of a thing"; we could be capturing the *semantics* in the type system better. – Eric Lippert Feb 13 '19 at 16:42
  • @EricLippert In C++ they are kind of wrapped by a typedef of `size_t` which is unsigned long long int – nada Feb 13 '19 at 16:42
  • @EricLippert Still, why allowing these invalid / negative indices and working around it by wrapping it in `try` and `catch`, when you could just disallow them and actually using a logical, intuitive type for indexing: The biggest possible platform available whole number without negatives. – nada Feb 13 '19 at 16:55
  • 1
    Whoa, hold on a minute. You *never* wrap a try-catch around an indexing operation, *ever*. You are required to ensure that the index is in range; you don't hope that it is, and catch your mistake if it is not. – Eric Lippert Feb 13 '19 at 17:14
  • 4
    Regardless: your best bet for being successful in C# is to pretend the unsigned types do not exist. **They are in C# only for interoperability with other languages**. You do not use them for anything other than that. In particular, you never say "this quantity is logically non-negative, therefore I will use an unsigned type" in C#. – Eric Lippert Feb 13 '19 at 17:15
  • @EricLippert Ah thank you, I think your last comment made the reasoning more clear to me than anything else I read here XD You didn't tell me if my answers to your exercise were correct :( – nada Feb 13 '19 at 17:22

0 Answers0