A clean interview-ready explanation of C# array append performance: why arrays cannot grow in place, what Array.Resize does, when List T is the better choice.
An interviewer asks: "Can you append to a C# array?" Most candidates pause, then say something like "yeah, with Array.Resize" — and that's where the answer starts to fall apart. Understanding C# array append interview performance is less about memorizing syntax and more about knowing exactly what happens in memory when you try to grow something that was never designed to grow.
The good news is that the correct answer is short. Arrays in C# are fixed-size. You cannot append in place. Every "append" is actually an allocation, a copy, and an assignment — and the cost is O(n). If you can say that cleanly and follow it with a sentence about when to reach for `List<T>` instead, you've already separated yourself from most candidates at the mid-level.
This article gives you the model answer first, then the mechanics and performance tradeoffs behind it.
Give the Interview Answer First, Then Explain the Cost
The one sentence that gets you through the interview
"C# arrays are fixed-size, so you can't append in place — appending means allocating a new array, copying the existing elements, and writing the new value, which is O(n)."
That's the answer. Say it confidently, without qualification, and without immediately apologizing for it. Interviewers are not looking for you to hedge with "well, it depends on the scenario." The fixed-size constraint is not an edge case — it's the defining property of arrays in .NET, documented explicitly in Microsoft's array documentation.
What this looks like in practice
Say you have `int[] scores = { 10, 20, 30 }` and you need to add a fourth value. A strong candidate doesn't reach for `Array.Resize` without comment. They say: "I'd need to allocate a new array of length 4, copy the three existing values, and assign the fourth. That's the only honest path with a raw array. If I expect more appends after this, I'd switch to `List<T>` instead — it handles the growth internally with amortized O(1) append cost."
That answer demonstrates the constraint, names the cost, and pivots to the right tool without sounding like a textbook. It doesn't sound memorized because it's structured around a decision, not a definition.
Why interviewers keep asking this question
The question is a proxy for three things. First, does the candidate understand that arrays are not dynamic by default? Second, do they know what copying actually costs? Third, can they name a better tool for growth-heavy workloads without being prompted?
A candidate who says "just use Array.Resize" and stops there has answered one-third of the question. The interviewer wants to hear the copy cost, the O(n) implication, and the `List<T>` alternative — because those three pieces together show that the candidate thinks about data structures in terms of the work they're actually doing, not just the syntax they remember.
C# Arrays Are Fixed-Size, and That Is the Whole Point
Why "just grow it" is not how arrays work
An array in .NET is a contiguous block of memory allocated at creation time with a fixed length. When you write `new int[5]`, the runtime reserves exactly enough space for five integers, laid out side by side. There's no slack at the end, no reserved capacity, no mechanism to extend the block. The length is baked in.
This is not a limitation the language forgot to fix — it's a deliberate design that makes arrays extremely fast for indexed reads and writes. The tradeoff is that C# array resizing is not a stretch operation. You cannot ask the runtime to extend the same memory block because, in most cases, the memory immediately after the array is already in use by something else.
What this looks like in practice
You have `int[] ids = { 1, 2, 3, 4, 5 }` and you need to store a sixth ID. There is no `ids.Append(6)`. There is no `ids.Length++`. The only path is: create a new `int[]` of length 6, copy the five existing values into it, assign the sixth value, and point your variable at the new array. The old array becomes eligible for garbage collection.
This is not a quirk of a specific API — it's the consequence of the contiguous-layout memory model that .NET arrays use. According to the .NET runtime documentation, arrays are fixed-length, index-based structures, and that property is what makes them suitable for scenarios where size is known and access patterns are random rather than append-heavy.
Array.Resize Still Copies Everything You Already Had
The part people miss about resizing
`Array.Resize` looks like it grows an array. The name implies in-place expansion. Candidates who've seen it used in code often assume it's a cheap operation — a kind of `realloc` equivalent that nudges the memory boundary outward.
It isn't. `Array.Resize<T>` in C# allocates a brand-new array of the requested size, copies all existing elements from the old array into the new one, and then updates the reference. The old array is discarded. The Microsoft documentation for Array.Resize states this directly: if the new size is larger than the current length, a new array is allocated and the elements are copied.
What this looks like in practice
Start with `string[] tags = { "api", "auth", "cache" }`. You call `Array.Resize(ref tags, 6)`. What happens: a new `string[6]` is created, the three existing strings are copied into positions 0, 1, and 2, and positions 3 through 5 are initialized to null. The variable `tags` now points to the new array. The original three-element array is gone.
In code, that looks like this:
Every one of those original strings was copied. If the array had 10,000 elements instead of 3, all 10,000 would be copied. The cost scales linearly with the number of existing elements — that's the O(n) in the interview answer.
What this costs in real code
If you call `Array.Resize` inside a loop — say, appending one item per iteration — you pay O(n) on every iteration. A loop of n iterations becomes O(n²) total work. This is the exact pattern interviewers are probing for when they ask the append question. A candidate who says "I'd resize in a loop" has just described a quadratic algorithm for what could be a linear one.
Assignment, Copying, and Resizing Are Not the Same Thing
The quick way to tell these apart
Three operations sound related but have completely different performance profiles:
- Index assignment (`array[i] = value`) writes a value to an existing slot. O(1). No allocation, no copying.
- Array.Copy / CopyTo transfers elements from one array to another that already exists. O(n) for the number of elements copied, but no allocation — the destination must already be the right size.
- Resize-driven copying allocates a new array and then copies into it. O(n) plus allocation overhead.
Blurring these three is how a candidate sounds fuzzy on performance. The distinction matters because one of them is free and two of them are not.
What this looks like in practice
`Array.Copy` and `Array.CopyTo` are documented in Microsoft's Array API reference and both require the destination to already exist at the correct size. They don't allocate — the caller is responsible for that. `Array.Resize` bundles the allocation and the copy together, which is convenient but hides the cost.
Why this matters in interviews
A strong candidate can point to `array[i] = value` and say "that's O(1), it touches one memory location." They can point to `Array.Copy` and say "that's O(n), but it's a fast bulk move with no allocation." They can point to `Array.Resize` and say "that's O(n) plus allocation — and if you do it in a loop, you've written O(n²) code without realizing it." That progression — from cheapest to most expensive, with the reason for each — is exactly what separates a candidate who knows the API from one who understands the cost model.
List\<T\> Wins When Appends Keep Happening
Why List\<T\> usually beats an array for growth
`List<T>` is not faster than an array at any individual operation. For indexed reads, a raw array is at least as fast. What `List<T>` does differently is manage capacity separately from length. When you add an item and the internal array is full, `List<T>` doesn't resize by one — it doubles the capacity. That doubling strategy means the expensive reallocation happens logarithmically often, not on every append.
The result is amortized O(1) append cost. Over a long sequence of appends, the average cost per operation approaches O(1) even though individual reallocations are O(n). This is why `List<T>` vs array is not a question of which is faster in isolation — it's a question of which fits the workload shape. According to the List\<T\> documentation, capacity grows by doubling when the internal array is exhausted.
What this looks like in practice
You're building a backend endpoint that collects matching records from a database query where the count isn't known ahead of time. Using an array means either overallocating (wasteful) or resizing on each addition (O(n²) in the worst case). Using `List<T>` means calling `.Add()` in a loop and letting the capacity-doubling logic handle growth. When you're done, `.ToArray()` gives you the final fixed-size result if the caller needs it — one allocation, one copy, at the end.
Where the tradeoff flips
Arrays still win when the final size is known before you start, when memory layout matters for interop or unsafe code, or when the data is read far more often than it's written. A lookup table, a fixed-length buffer, a results set whose size you computed from a query count — all of these are better as arrays. The rule isn't "always use `List<T>`." It's "use `List<T>` when the size is uncertain and growth is frequent."
Preallocate the Array When You Already Know the Size
The fastest path is often the boring one
If you know the final element count before you start filling the array, allocate once and use array index assignment by position. No resizing, no copying, no capacity management. Just `new T[n]` and then a loop that fills each slot directly. This is the fastest pattern for building an array from a known-size source.
The performance case is simple: one allocation, zero copies, O(1) per write. There's no growth logic to trigger, no intermediate arrays to garbage-collect, no amortization required because there's nothing to amortize.
What this looks like in practice
You're deserializing a fixed-length payload from an API response that includes a `count` field. You know before you start iterating that there will be exactly `count` items. The right pattern:
No `List<T>`, no `.Add()`, no `.ToArray()` at the end. One allocation, filled by index. In benchmarks on .NET 8, this pattern consistently outperforms `List<T>.Add()` followed by `.ToArray()` for known-size workloads — the difference is typically small in absolute terms but measurable when the operation is in a hot path. The methodology matters: test on your actual runtime, with realistic data sizes, before optimizing prematurely.
Answer the Amortized Append Follow-Up Without Bluffing
The follow-up they are really testing
"So if `List<T>` uses an internal array, doesn't it still have to copy when it grows?" Yes — and a strong candidate says so directly. The follow-up isn't a trap, it's an invitation to show you understand amortized complexity rather than just repeating "List is O(1) append."
The honest answer: each individual reallocation in `List<T>` is O(n) for that operation. But because the capacity doubles each time, the total number of copy operations across all appends is bounded by 2n. Spread across n appends, that's O(1) per append on average. The occasional expensive reallocation is real — it just doesn't change the overall cost shape of a long append sequence.
What this looks like in practice
"What happens when the list grows past capacity?" Walk through it: the internal array is full, `.Add()` triggers a reallocation, a new array of double the current capacity is allocated, all existing elements are copied, the new element is written. Then the list continues accepting appends at O(1) until it fills again. The doubling strategy ensures that each element is copied at most O(log n) times over its lifetime, which is where the amortized O(1) comes from.
The trap to avoid
Don't say "arrays and lists are basically the same thing, just use whichever is more convenient." They are not interchangeable. The interview is specifically testing whether you know the cost you're paying — and that cost is fundamentally different depending on whether growth is expected. Treating them as equivalent signals that you haven't thought about the performance model at all.
The Interviewer Traps Are There to Hear What You Leave Out
When a candidate sounds smart but says the wrong thing
The most common version of this: "You can append to an array in C# using `Array.Resize`." Technically true in the sense that the API exists and the result looks like a longer array. Functionally wrong in the sense that it omits the copy cost, the fixed-size constraint, and the reason you'd want a different data structure for growth-heavy workloads.
Interviewers hear this answer and immediately follow up: "And what does that cost?" A candidate who said "Array.Resize" without thinking about cost now has to reconstruct the answer under pressure. A candidate who led with the fixed-size constraint and the O(n) copy never needed the follow-up to rescue them.
What this looks like in practice
Interviewer: "So why not just resize every time you need to add an item?"
Weak answer: "It's a bit slow, but it works."
Strong answer: "Because every resize copies all existing elements into a new array — that's O(n) per resize. If you're resizing in a loop of n iterations, you've written O(n²) total work. That's the exact pattern `List<T>` was designed to avoid. Its capacity-doubling strategy means you only pay the copy cost occasionally, which gives you amortized O(1) per append instead."
The strong answer names the complexity, explains why it compounds, and names the tool that solves it. It doesn't require the interviewer to drag the information out piece by piece.
How to finish strong
The best closing summary for this kind of question: "Arrays are the right choice when size is known up front and access is mostly reads. `List<T>` is the right choice when you're appending repeatedly and don't know the final size. The difference isn't which one is faster — it's which one fits the shape of the work you're actually doing."
A brief rubric for interviewers: a weak answer mentions `Array.Resize` without cost; an acceptable answer explains the O(n) copy; a strong answer explains amortized O(1) for `List<T>`, knows when arrays still win, and can walk through what happens during a reallocation without being prompted. According to engineering hiring resources like SHRM's technical interview guidance, the distinguishing factor in strong technical answers is always the ability to connect mechanism to cost to decision — not just reciting the API.
FAQ
Q: Why can't a C# array be appended to in place?
Arrays in .NET are allocated as contiguous fixed-size memory blocks at creation time. The runtime reserves exactly the space requested — no more. There is no mechanism to extend that block because adjacent memory is typically in use by other objects. Any "append" requires a new allocation and a full copy of existing elements.
Q: What happens internally when you resize an array in C#?
`Array.Resize<T>` allocates a new array of the requested length, copies all elements from the old array into the new one using a bulk copy operation, and updates the reference variable to point at the new array. The original array becomes eligible for garbage collection. The copy cost is O(n) where n is the number of existing elements.
Q: Why is `List<T>` usually the better choice for adding items repeatedly?
`List<T>` maintains a separate internal capacity that grows by doubling when exhausted. This means expensive reallocations happen logarithmically often rather than on every append. The result is amortized O(1) append cost over a long sequence — far better than the O(n²) total cost of resizing an array on every addition.
Q: When is assigning by index to a preallocated array the fastest option?
When you know the final element count before you start filling the structure. One allocation, zero intermediate copies, O(1) per write. This pattern avoids all growth logic and typically outperforms `List<T>.Add()` followed by `.ToArray()` for fixed-size workloads because it eliminates the capacity management overhead entirely.
Q: How do Array.Copy, Array.CopyTo, Array.Resize, and List\<T\>.ToArray differ in cost and behavior?
`Array.Copy` and `Array.CopyTo` transfer elements between two already-allocated arrays — O(n) copy, no allocation. `Array.Resize` allocates a new array and then copies — O(n) plus allocation overhead. `List<T>.ToArray()` allocates a new array of exactly `Count` length and copies all elements — O(n) plus allocation, but done once at the end of a growth sequence rather than on every addition.
Q: What performance tradeoff should a backend developer consider when choosing between array and list?
Arrays give you the best performance for random indexed access and known-size scenarios. `List<T>` gives you better performance for repeated appends where size is uncertain. The wrong choice is using an array with repeated `Array.Resize` calls — that's the worst of both worlds: the inflexibility of an array with the repeated copy cost of unbounded growth.
Q: What should an interviewer expect a strong candidate to say about array immutability, copying, and amortized append performance?
A strong candidate should explain that arrays are fixed-size by design, that any append requires a new allocation and O(n) copy, that `Array.Resize` is a convenience wrapper around that same pattern, that `List<T>` solves repeated appends through capacity doubling and amortized O(1) cost, and that preallocated arrays beat both when size is known. They should be able to walk through what happens during a `List<T>` reallocation without being prompted.
How Verve AI Can Help You Ace Your Software Engineer Coding Interview
The structural problem this article just walked through — knowing the mechanism, knowing the cost, and being able to connect both under live questioning — is exactly what most candidates can't rehearse from documentation alone. Reading about amortized O(1) is different from explaining it out loud while someone is watching your face for hesitation.
Verve AI Interview Copilot is built for that gap. It listens in real-time to the actual conversation as it happens — not a scripted mock — and responds to what you actually said, not a canned prompt. If you say "Array.Resize" without mentioning the copy cost, Verve AI Interview Copilot can surface the follow-up before the interviewer does, so you practice recovering cleanly rather than discovering the gap during the real thing. The tool stays invisible while it works, so your screen share stays clean and your attention stays on the answer. For a question like C# array append performance — where the real test is the follow-up, not the opener — Verve AI Interview Copilot gives you the reps that documentation can't.
Conclusion
You walked into this article with a question an interviewer might ask in the next 48 hours. You're walking out with the answer: arrays are fixed-size, appending means allocating and copying, that's O(n), and if you're appending repeatedly, `List<T>` is the right tool because its capacity-doubling strategy gives you amortized O(1) instead of O(n²).
Memorize that one sentence. Then practice the follow-up — "why not just resize every time?" — until you can explain capacity doubling and amortized cost without pausing to reconstruct it. That's the answer that separates a candidate who knows the API from one who understands the performance model. The interviewer is listening for exactly that difference.
James Miller
Career Coach

