Reputation: 21865
Given this simple synchronization code :
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Mutex
{
class CalcArrayThreads
{
private static System.Threading.Mutex mutex = new System.Threading.Mutex();
private const int ThreadsNumber = 10;
private static List<int> list = new List<int>(10);
public static void SumPartialArray(object index)
{
mutex.WaitOne();
int indexBoxed = (int) index;
int sum = 0;
for (int i = indexBoxed; i < indexBoxed + 9; i++)
{
sum += i;
}
Console.WriteLine(string.Format("Thread : {0} Calculated value : {1}", Thread.CurrentThread.Name, sum));
// list.Add(sum);
list[indexBoxed] = sum;
mutex.ReleaseMutex();
// Console.WriteLine(list.Count());
}
static void Main(string[] args)
{
for (int i = 0; i < ThreadsNumber; i++)
{
Thread myThread = new Thread(new ParameterizedThreadStart(SumPartialArray));
myThread.Name = String.Format("Thread{0}", i + 1);
myThread.Start(i);
}
Console.Read();
}
}
}
When I use the line :
list[indexBoxed] = sum;
I get :
Unhandled Exception: System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Even though the capacity of the list is 10 .
Why ?
Upvotes: 1
Views: 1916
Reputation: 66449
If you just did the following with an array, it'd work as expected, setting the second element:
int[] list = new int[10];
list[2] = 5; // second element is 5, no exception thrown
Looking at the List<T>
constructor, when you pass in a capacity, it's actually doing something very similar using an internal array:
this._items = new T[capacity];
So it seems like it should work then, when you try to access any element less than capacity, but here's how the indexer is implemented:
public T this[int index]
{
get
{
if (index >= this._size)
throw new ArgumentOutOfRangeException("...");
return this._items[index];
}
set
{
if (index >= this._size)
throw new ArgumentOutOfRangeException("...");
this._items[index] = value;
}
}
It's actually checking a _size
variable first, and throwing the exception if the index
you're requesting is larger than it. If it weren't for that check, it'd work as you expect.
_size
is initialized to 0 unless you pass a non-empty collection into the constructor, and changes value when you use the Add
, RemoveAt
, Clear
, etc methods. Internally, it's still just using an array to store the elements, and if _size
is greater than the capacity (say, after trying to Add
one more element), it allocates a new array and copies all the elements from the older (smaller) array into it.
I see two solutions you could consider using:
Either just use an array, like this:
private static int[] list = new int[10];
Or supply a collection of some default value (here, a bunch of zeroes) via the constructor:
private static List<int> list = new List<int>(Enumerable.Repeat(0, 10));
Upvotes: 4
Reputation: 511
Since you have not added anything to the list yet. It will always be 0
.
You actually need to use Count
property to get the number of elements in the list.
A remark from MSDN:
Capacity is the number of elements that the List can store before resizing is required, whereas Count is the number of elements that are actually in the List.
Capacity is always greater than or equal to Count. If Count exceeds Capacity while adding elements, the capacity is increased by automatically reallocating the internal array before copying the old elements and adding the new elements.
Upvotes: 2
Reputation: 8466
The value you are passing in the constructor is the initial capacity which then is increased dynamically as the list grows.
But the list is still empty. And using the index accessor on an empty list will yield the exact exception you are seeing.
To add elements to the list use Add method instead and don't worry about the capacity. It grows internally.
EDIT: As to the title of your question, I belive that the Count is allways 0 (you are not adding elements to it) and not the Capacity.
Upvotes: 1