While the answer to this question is excellent, it implies that you should surround calls to List.ToArray() in a lock for concurrency. this blog post also implies that it could fail catastrophically (but rarely). I typically use ToArray rather than a lock in when enumerating Lists or other collections in order to avoid the "Collection Modified, Enumeration may not complete" exception. This answer and the blog post have called that assumption into question.
The documentation for List.ToArray() does not list any exceptions, so I have always assumed that it will always complete (albeit maybe with stale data) and that while it is not thread safe from a data consistency point of view, it is thread safe from a code execution point of view - in other words, it won't throw an exception and calling it won't corrupt the internal data structure of the underlying collection.
If this assumption isn't correct, then while it has never caused a problem it could be a timebomb in a high availability application. What is the definitive answer?
No, you will always get a new copy of the array, though the objects in it aren't copies, they are the same references as in the original array.
Yes, iterating a copy of the list is always thread-safe, as long as you use a lock around the ToArray() method. Note that you still need the lock, no structural improvement. The advantage is that you'll hold the lock for a short amount of time, improving concurrency in your program.
You will not find documentation about possible exceptions of ToArray
method for one simple reason. This is an extension method that has many 'overloads'. They all have same method signature, but implementation is different for different collection types, e.g. List<T>
and HashSet<T>
.
However, we can make a safe assumption for most of the code that .NET framework BCL does not perform any locking for performance reasons. I've also checked very specifically implementation of ToList
for List<T>
.
public T[] ToArray()
{
T[] array = new T[this._size];
Array.Copy(this._items, 0, array, 0, this._size);
return array;
}
As you might have imagined, it's quite simple code which ends up executing in mscorlib
.
For this specific implementation, you can also see exceptions which could occur in MSDN page for Array.Copy method. It boils down to an exception which is thrown if rank of the list changes right after destination array was just allocated.
Having in mind that List<T>
is trivial example, you can imagine that chances for exception rise on structures which require more complicated code in order to store in an array. Implementation for Queue<T>
is a candidate which is more likely to fail:
public T[] ToArray()
{
T[] array = new T[this._size];
if (this._size == 0)
{
return array;
}
if (this._head < this._tail)
{
Array.Copy(this._array, this._head, array, 0, this._size);
}
else
{
Array.Copy(this._array, this._head, array, 0, this._array.Length - this._head);
Array.Copy(this._array, 0, array, this._array.Length - this._head, this._tail);
}
return array;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With