-4

Firstly I know that I can't use a foreach loop in C# to add values in let's say an array... But why? Why for example I can't do this

int[] numbers = { 1, 4, 3, 5, 7, 9 };
foreach (int item in numbers)
{
    numbers[item] = 2;
}

Does it have something to do with the actual realisation of the foreach loop in the back-end? And how does the foreach loop exactly work? I know that it goes through the whole collection(array) but how exactly?

john
  • 23
  • 4
  • 2
    What exactly do you expect this to do? It will fail because you try to access index 7 and 9 of an array that is only 6 long. *Array sizes are fixed in C#.* – DavidG Feb 05 '17 at 20:16
  • 1
    you can't add items to an array regardless of the loop type you use. – Selman Genç Feb 05 '17 at 20:17
  • @SelmanGenç of course i can ... `int[] numbers = { 1, 4, 3, 5, 7, 9 }; for (int i = 0; i < numbers.Length; i++) { numbers[i] = 10; }` – john Feb 05 '17 at 20:19
  • @DavidG A little explaination please? I didn't quite get you – john Feb 05 '17 at 20:20
  • 4
    That isn't adding items, it's overwriting existing ones. You cannot add new items. – DavidG Feb 05 '17 at 20:20
  • 2
    Be aware that unlike e.g. JavaScript's `foreach`, C#'s `foreach` enumerates *values*, not their *indices*. – O. R. Mapper Feb 05 '17 at 20:21
  • @O.R.Mapper I'm confused lol... isn't a foreach loop same as a for loop but the difference is that the foreach loop with go through the whole collection(array) ? – john Feb 05 '17 at 20:23
  • 1
    @john A "regular" for loop gives you successive `int`s from index `0` to `Length - 1`. With a `foreach` loop, you don't know the index and only have the current value. – Salem Feb 05 '17 at 20:24
  • 2
    On your 5th iteration of the loop, `item` becomes the value 7 because that's the value of the item in the 5th position in your array. Then you try to access the 7th value in your array with `numbers[item]` which doesn't exist. – DavidG Feb 05 '17 at 20:25
  • @john: Not quite. In C#, a `foreach` loop could be seen as the "same" as a `for` loop, where you automatically get the current item in a variable you specify. – O. R. Mapper Feb 05 '17 at 20:25
  • Possible duplicate of [Why is The Iteration Variable in a C# foreach statement read-only?](http://stackoverflow.com/questions/776430/why-is-the-iteration-variable-in-a-c-sharp-foreach-statement-read-only) – Mathias R. Jessen Feb 05 '17 at 20:26
  • 2
    @MathiasR.Jessen: This is not a duplicate. The other question is asking for a rationale why one cannot assign a value to the iteration variable in a `foreach` loop (which would then be propagated to the underlying collection). This question asks either why the indicated way of reading from the iteration variable does not work (which appears to be based upon the assumption that the iteration variable contains the index rather than the value), or why no items can be added to an array. – O. R. Mapper Feb 05 '17 at 20:28
  • Why not just initialize the whole thing to 2 in the first place if that's what you want? – EJoshuaS - Stand with Ukraine Feb 05 '17 at 20:33
  • @EJoshuaS It's easy to imagine this being a simplified version of a more complex problem. This specific example might not be exactly what the OP is trying to achieve. – Salem Feb 05 '17 at 20:33
  • 1
    Maybe you should try with `string[] numbers = { "one", "four", "three", "five", "seven", "nine", };` instead. You seem to be confused with `int[]` because the element type is the same type (`int`) as the type we use with the indexer. With a `string[]` that does not happen. – Jeppe Stig Nielsen Feb 05 '17 at 20:34
  • @EJoshuaS ... Well i was just wondering why isn't possible to use a foreach loop to assign some values at some positions just like a for loop... this was only an example – john Feb 05 '17 at 20:34
  • @1blustone I assume that that's what he was doing, but it's not clear from the post. – EJoshuaS - Stand with Ukraine Feb 05 '17 at 20:34
  • You can do `foreach (var item in numbers) { numbers[2] = item; }`, that will work. But what do you want? – Jeppe Stig Nielsen Feb 05 '17 at 20:37
  • @JeppeStigNielsen that will definitely not do what was requested. You're repeatedly setting the third element. – Salem Feb 05 '17 at 20:41

4 Answers4

4

You are passing in the value of an item (your variable, item, will be the value of the array at each position in sequence, not the index) in the array as the index. The index used there is meant to be the position of the item you are attempting to access, not the value. So each iteration of the loop you are calling:

  • numbers[1]
  • numbers[4]
  • numbers[3]
  • numbers[5]
  • numbers[7]
  • numbers[9]

The array has 6 numbers, so when you get to numbers[7], you are asking for a value that is not there, hence the exception.

A better method of doing what you are trying to do would be:

for(int i = 0; i < numbers.Length; i++)
{
    numbers[i] = 2;
}

On each iteration of this loop you would be accessing:

  • numbers[0]
  • numbers[1]
  • numbers[2]
  • numbers[3]
  • numbers[4]
  • numbers[5]
pquest
  • 3,151
  • 3
  • 27
  • 40
3

You need to step through your code in a debugger.

A for statement is more like a while statement, not like a foreach.

The line int[] numbers = { 1, 4, 3, 5, 7, 9 }; create this:

numbers[0] = 1;
numbers[1] = 4;
numbers[2] = 3;
numbers[3] = 5;
numbers[4] = 7;
numbers[5] = 9;

Your foreach statement does this:

numbers[1] = 2;
numbers[4] = 2;
numbers[3] = 2;
numbers[5] = 2;
numbers[7] = 2; <- this line overflows your array!
numbers[9] = 2; <- and so would this.

You have to learn the difference between an array index and an array value.

Dour High Arch
  • 21,513
  • 29
  • 75
  • 90
  • I know "the difference between an array index and an array value" lol... just don't know how the foreach loop exactly works in the implementation – john Feb 05 '17 at 20:37
  • @john It's been demonstrated multiple times. It's equivalent to a for loop, where you store the current value. `for(int i = 0; i < array.Length; i++) { int currentValue = array[i]; }` – Salem Feb 05 '17 at 20:40
  • The `numbers[9]` part isn't actually a problem since the value there is changed to `2` before the code runs. But the `7` still blows up. – Joel Coehoorn Feb 05 '17 at 20:40
3

I'm looking at this:

numbers[item] = 2;

In this expression, you're using the item variable like an index, as if it had the values 1, 2, 3, 4, etc. That's not how the foreach iteration variable works for C#. The only language I know that does it this way is Javascript.

Remember that foreach and for are not the same thing. Just about every other language, including C#, gives you the actual array values in the item variable of a foreach loop: 1,4, 3, 5 etc. Now, these are integers, so you could try to use them as indexes. You can run the loop for a while like that... until you get to the value 7. At this point, your array only has six values. You're trying to do this:

numbers[7] = 2;

for an array where the largest valid index you can use is 5.

This is true even taking your modification of the array into account. Let's look at the array after each iteration through the loop:

{ 1, 4, 3, 5, 7, 9 }  //initial state
{ 1, 2, 3, 5, 7, 9 }  // after 1st iteration (index 0). Value at index 0 is 1, so item as index 1 is set to 2
{ 1, 2, 2, 5, 7, 9 }  // after 2nd iteration (index 1). Value at index 1 is now 2, so item at index 2 is set to 2
{ 1, 2, 2, 5, 7, 9 }  // after 3rd iteration (index 2). Value at index 2 is now 2, so item at index 2 is set to 2
{ 1, 2, 2, 5, 7, 2 }  // after 4th iteration (index 3). Value at index 3 is 5, so item at index 5 is set to 2
// The 5th iteration (index 4). Value at index 4 is 7, which is beyond the end of the array

For the why of this... it sounds like you're used to a more dynamic language. Some these other languages, like php or Javascript, don't have real arrays at all in the pure computer science sense. Instead, they have collection types they'll call an array, but when you get down to it are really something different.

C# has real arrays, and real arrays have a fixed size. If what you really want is a collection, C# has collections, too. You can use List<T> objects, for example, to get an array-like collection you can append to easily.

For the other languages, the results vary depending on what you're talking about, but for the most permissive the result of your 5th iteration is something like this:

{ 1, 2, 2, 5, 7, 2,  ,2 } 

Note the missing value at index 6. That kind of things leads to mistakes that slip through your tests and don't show up until run-time. You also need to start wondering just how densely or sparsely the array will be filled, because the best strategy for handling these arrays can vary wildly depending on your answer... everything from just a big backing array with empty nodes that the programmer has to know about all the way to Hashtables and Dictionaries. And, by the way, C# again has these options available to you.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • ok but `{ 1, 2, 3, 5, 7, 9 } // after 1st iteration. Index at 1st value (1) set to 2` why does it start from the index 1, why not index 0? – john Feb 05 '17 at 20:42
  • I was talking sequentially, rather than by index. The first value in the array is at position `0`, and has a value of `1`. So after the first iteration, the item at index position `1` is set to have a value of `2`. I'll try to clarify. – Joel Coehoorn Feb 05 '17 at 20:44
  • So that's why "You can't use an foreach loop for assigning values in an array" as it is said in msdn haha – john Feb 05 '17 at 21:01
-1

You need to create counter, in other case you trying to access item outside of array

int[] numbers = new int[]{ 1, 4, 3, 5, 7, 9 };
int i = 0;
foreach (int item in numbers)
{
    numbers[i] = 2;
    i++;
}

// Print the items of the array
foreach (int item in numbers)
{
    Console.WriteLine(item);
}
Bennik2000
  • 1,112
  • 11
  • 25
Victor Leontyev
  • 8,488
  • 2
  • 16
  • 36
  • Yea this is my question... why am I accessing item outside the array? Does it have to do something with the implementation of the foreach loop? – john Feb 05 '17 at 20:21
  • @john: What concrete operation do you mean by "accessing item outside the array", where it is not clear to you that you are using an index outside of the range of valid indices for the array? Your array has a length of 6, hence your array indices must be between 0 and 5 (both inclusive) to be "within" the array. – O. R. Mapper Feb 05 '17 at 20:23
  • well if i do this `int[] numbers = { 1, 4, 3, 5, 7, 9 }; foreach (int item in numbers) { numbers[item] = 2; }` I get an IndexOutOfRangeException – john Feb 05 '17 at 20:24
  • @john: Have you checked the values that `item` assumes in each of your iterations? You will find that it will have the values `7` and `9` at some point, both of which are not within the bounds of valid indices for your array. – O. R. Mapper Feb 05 '17 at 20:26
  • 1
    @john you are passing in the value of an item in the array as the index. So you are passing in numbers[1], numbers[4], numbers[3], numbers[5], numbers[7], numbers[9]. The collection has 6 numbers, so when you get to numbers[7], you are asking for a value that is not there... – pquest Feb 05 '17 at 20:27