Back to Blog
C#

C# LINQ ToArray: Usage and Performance

c# linq toarray: Learn how C# LINQ ToArray converts sequences to arrays, when to use it, and how it affects memory and performance.

LINQToArrayC#IEnumerablePerformance
Diagram showing an IEnumerable sequence being converted into a compact array using LINQ ToArray method.

When you call c# linq toarray, you are asking LINQ to materialize an IEnumerable<T> into a T[] array. This is a common operation, but its behavior and cost are often misunderstood. This article explains how ToArray works, when it is the right choice, and where it can cause unnecessary allocations.

What ToArray Actually Does

ToArray is an extension method defined in System.Linq. It takes an IEnumerable<T> and returns a T[] containing the same elements in the same order. The method forces immediate execution of any deferred query, meaning that if you have a lazy sequence (such as a LINQ query or a generator), calling ToArray will enumerate it completely and store the results.

The method is not lazy. It consumes the entire source sequence and builds an array. This is useful when you need a snapshot of the data or when you need to pass the result to code that expects an array.

Basic Usage and Syntax

The syntax is straightforward:

IEnumerable<int> numbers = Enumerable.Range(1, 10); int[] array = numbers.ToArray();

You can also use it directly on a query:

var filtered = items.Where(x => x.IsActive).ToArray();

The method works with any IEnumerable<T>, including arrays, lists, and custom iterators. It is a standard part of LINQ and available in all modern .NET versions.

How ToArray Works Internally

ToArray does not know the size of the source sequence in advance. It uses a dynamically growing buffer. When the source is an ICollection<T>, it can query Count and allocate the exact size. Otherwise, it starts with a small buffer and doubles it as needed. After enumeration, it copies the buffer into a new array of the exact length. This means there is at least one copy operation, and often several allocations.

The internal implementation is similar to how List<T> grows, but the final step differs. ToList can reuse the buffer as the list's internal array, while ToArray must copy into a new array of the exact size. This extra copy is a small overhead, but it can be significant for large sequences.

When ToArray Is the Right Choice

Use ToArray when you need a fixed-size, indexable collection that you will not resize. Arrays have lower overhead than List<T> when you only need read access. They also provide better cache locality for sequential access. If you need to pass data to an API that expects an array, ToArray is the direct conversion.

For example, if you are building a method that returns a read-only collection and you know the size will not change, an array is a clean and efficient choice. It also communicates intent: the consumer cannot accidentally add or remove elements.

ToArray vs ToList

Both materialize a sequence, but they produce different types. The table below compares them:

CriterionToArrayToList
Result typeT[]List<T>
ResizabilityFixed sizeCan add/remove
Memory overheadSlightly lower (no extra list metadata)Higher (list capacity, versioning)
Common useFixed-size data, API contractsDynamic collections, frequent modifications

The choice depends on whether you need to modify the collection after creation. If you only read, an array is more compact. If you need to add or remove elements, a List is more practical.

Performance and Memory Considerations

The main cost of ToArray is the allocation of a new array and the copying of elements. If the source is not an ICollection, the internal buffer grows, causing multiple allocations and copies. This can be wasteful for large sequences. In contrast, ToList uses the same growth strategy, but the final copy is avoided because the buffer itself becomes the list's internal array. However, ToList may overallocate capacity, leaving unused slots.

If you already have an array and call ToArray on it, the method returns a new array even if the source is already an array. This is a common mistake that causes an unnecessary copy. In .NET, ToArray on an array returns a shallow copy, not the same reference.

For large sequences, the double copy (buffer growth and final copy) can be a bottleneck. If you know the size in advance, you can avoid the growth by using a source that implements ICollection<T>, such as a List<T> or another array. In those cases, ToArray allocates the exact size and copies once.

Common Pitfalls and Edge Cases

  • Calling ToArray on an empty sequence returns an empty array, not null.
  • ToArray does not deep-copy the elements; it only copies references. If the elements are mutable, changes to the original objects are reflected in the array.
  • ToArray forces enumeration of the entire sequence. If the sequence is infinite or has side effects, this can cause unexpected behavior or an infinite loop.
  • The method is not lazy; it materializes the entire sequence at once. This can be a problem for very large datasets that could be processed in a streaming fashion.

Another edge case is when the source sequence throws an exception during enumeration. The exception propagates, and the partially built array is discarded. There is no way to recover the partial results.

Alternatives to ToArray

If you need an array but want to avoid the double allocation, you can create an array manually when the size is known:

var list = new List<int>(); // ... fill list int[] array = new int[list.Count]; list.CopyTo(array);

But this is more verbose. In most cases, ToArray is fine. For large sequences, consider using ToList and then calling ToArray if you must, but that still copies.

Another alternative is to use Enumerable.ToArray with a known-size source like an ICollection, which avoids the buffer growth. You can also write a custom materialization method if you need to control the buffer growth strategy, but that is rarely worth the complexity.

When Not to Use ToArray

Avoid ToArray when you only need to iterate once. Streaming with foreach or using LINQ operators like Select without materialization is more memory-efficient. Also, if you need to pass a sequence to a method that only requires IEnumerable, keep it lazy.

For example, if you are chaining multiple LINQ operations and only the final result is needed, applying ToArray early forces the entire pipeline to execute before the next operation. This can break streaming and increase memory usage. In such cases, defer materialization until the end.

If you need to return a read-only collection from a method, consider returning IReadOnlyList<T> or IReadOnlyCollection<T> instead of an array. This gives you the flexibility to change the internal representation later without breaking callers. An array is a concrete type that exposes a mutable Length and allows element assignment, which may not be desirable.

c# linq toarray: Practical Usage and Code Examples | RYUSLOG DEV