C#
arrays
interfaces
programming
.NET

What interfaces do all arrays implement in C?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

In C#, arrays are more than fixed-size containers. They implement several interfaces that make them usable with generic and non-generic APIs, LINQ, and collection-based libraries. Understanding which interfaces are available helps you choose the right abstractions and avoid invalid assumptions about mutability.

Core Sections

1) Core interfaces implemented by arrays

All C# arrays derive from System.Array and implement interfaces such as:

  • IEnumerable
  • IEnumerable<T>
  • ICollection
  • ICollection<T>
  • IList
  • IList<T>
  • IReadOnlyCollection<T>
  • IReadOnlyList<T>

You can verify this at runtime.

csharp
1using System;
2using System.Linq;
3
4var interfaces = typeof(int[]).GetInterfaces()
5    .Select(i => i.FullName)
6    .OrderBy(x => x);
7
8foreach (var i in interfaces)
9    Console.WriteLine(i);

2) Array mutability through interfaces

Arrays are fixed-length, but elements are mutable (for reference or value replacement).

csharp
int[] a = { 1, 2, 3 };
a[1] = 99; // valid

However, operations that change size are not supported, even when cast to IList<T>.

csharp
IList<int> list = a;
// list.Add(4); // throws NotSupportedException

3) Covariance caveat with reference-type arrays

Reference-type arrays are covariant, which can lead to runtime exceptions.

csharp
string[] s = new string[1];
object[] o = s;
// o[0] = 123; // ArrayTypeMismatchException

Generic collections like List<T> avoid this specific runtime mismatch model.

4) When to use arrays vs List

Use arrays when:

  • size is fixed,
  • contiguous memory matters,
  • interop APIs expect arrays.

Use List<T> when dynamic resizing and collection operations (Add, Remove) are needed.

Validation and Production Readiness

After implementing any fix or pattern from this topic, validate behavior using a repeatable workflow rather than ad hoc spot checks. The most reliable process has three stages: reproduce baseline behavior, apply one focused change, then verify both expected and adjacent scenarios. This avoids false confidence from a single green run and helps isolate which change actually solved the problem.

A practical command-driven template:

bash
1# 1) capture baseline output/state
2./run_case.sh > before.txt
3
4# 2) apply one focused change from this guide
5# edit code/config and keep the diff minimal
6
7# 3) verify behavior and compare outputs
8./run_case.sh > after.txt
9diff -u before.txt after.txt

If your project includes automated tests, convert the original failure into a regression test immediately. This is the fastest way to prevent the same issue from reappearing during later refactors, dependency upgrades, or environment changes.

bash
1# example quality gate sequence
2./lint.sh
3./test.sh
4./smoke.sh

Also validate edge cases explicitly. Many production defects occur not on the nominal path, but on boundary inputs such as empty collections, null/none values, unusual encodings, or large payloads. Define a compact table of edge scenarios and expected outcomes so reviewers can reproduce your checks quickly.

Before rollout, confirm environment parity. A fix that works in local development can fail in staging or production when runtime versions, OS behavior, file systems, networking, or resource limits differ. Capture version metadata and infrastructure assumptions in your PR or runbook.

bash
1# capture runtime context (example)
2python --version
3node --version
4dotnet --info

Finally, define rollback criteria before deployment. If metrics or logs indicate regressions, teams should know exactly which change to revert and what signals trigger that decision. This operational discipline turns one-off troubleshooting into a maintainable engineering practice and significantly reduces incident recovery time.

Common Pitfalls

  • Assuming IList<T> on arrays supports resizing operations.
  • Confusing fixed-size arrays with immutable collections.
  • Ignoring covariance risks with reference-type arrays.
  • Using arrays in APIs where caller expects dynamic growth behavior.
  • Overlooking array interface support and writing unnecessary adapter code.

Summary

C# arrays implement many collection interfaces, including generic and read-only variants, which makes them broadly interoperable. They remain fixed-size containers despite interface breadth. Choose arrays for fixed-length scenarios and List<T> for dynamic collections, and watch for covariance-related runtime traps.


Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.