C#
array manipulation
selecting items
C# programming
coding techniques

Selecting a range of items inside an array 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

C# provides several ways to select a range of items from an array. Starting with C# 8.0, the range operator .. and Index type (^ for end-relative indexing) allow concise slicing syntax like array[1..4]. For earlier versions, Array.Copy(), ArraySegment<T>, LINQ's Skip().Take(), and Span<T> provide range selection. The range operator is the most readable approach for modern C# code.

Range Operator (C# 8.0+)

csharp
1int[] numbers = { 10, 20, 30, 40, 50, 60, 70 };
2
3// Select elements at index 1, 2, 3 (end index is exclusive)
4int[] slice = numbers[1..4];
5// Result: [20, 30, 40]
6
7// From start to index 3
8int[] first3 = numbers[..3];
9// Result: [10, 20, 30]
10
11// From index 4 to end
12int[] last3 = numbers[4..];
13// Result: [50, 60, 70]
14
15// Entire array copy
16int[] copy = numbers[..];
17// Result: [10, 20, 30, 40, 50, 60, 70]

The .. operator creates a Range value. The start index is inclusive, the end index is exclusive — matching the convention used by Python slicing and most languages.

Index from End (^)

csharp
1int[] numbers = { 10, 20, 30, 40, 50, 60, 70 };
2
3// ^1 = last element, ^2 = second to last, etc.
4int last = numbers[^1];       // 70
5int secondLast = numbers[^2]; // 60
6
7// Last 3 elements
8int[] lastThree = numbers[^3..];
9// Result: [50, 60, 70]
10
11// All except first and last
12int[] middle = numbers[1..^1];
13// Result: [20, 30, 40, 50, 60]
14
15// Last 4 but skip the last 1
16int[] segment = numbers[^4..^1];
17// Result: [40, 50, 60]

^n means "n positions from the end". ^0 is past the end (same as numbers.Length), so numbers[^3..^0] is equivalent to numbers[^3..].

ArraySegment<T> (Zero-Copy View)

csharp
1int[] numbers = { 10, 20, 30, 40, 50, 60, 70 };
2
3// Create a view into the array — no copy
4ArraySegment<int> segment = new ArraySegment<int>(numbers, 2, 3);
5// Offset 2, count 3: [30, 40, 50]
6
7foreach (int item in segment)
8    Console.Write($"{item} ");  // 30 40 50
9
10// Modifications affect the original array
11segment[0] = 99;
12Console.WriteLine(numbers[2]);  // 99 — original array changed

ArraySegment<T> wraps a portion of an array without copying. Changes to the segment modify the original array.

Span<T> (High-Performance Slicing)

csharp
1int[] numbers = { 10, 20, 30, 40, 50, 60, 70 };
2
3// Span is a zero-copy view with range support
4Span<int> span = numbers.AsSpan(2, 3);  // offset 2, length 3
5// Or: Span<int> span = numbers.AsSpan()[2..5];
6
7foreach (int item in span)
8    Console.Write($"{item} ");  // 30 40 50
9
10// Modify through the span
11span[0] = 99;
12Console.WriteLine(numbers[2]);  // 99 — original changed
13
14// ReadOnlySpan for read-only access
15ReadOnlySpan<int> readOnly = numbers.AsSpan(2, 3);
16// readOnly[0] = 99;  // Compile error

Span<T> is stack-allocated and provides the highest performance for array slicing. It cannot be stored in fields or used across await boundaries.

Array.Copy

csharp
1int[] numbers = { 10, 20, 30, 40, 50, 60, 70 };
2
3// Copy elements at index 2, 3, 4 into a new array
4int[] slice = new int[3];
5Array.Copy(numbers, 2, slice, 0, 3);
6// Result: [30, 40, 50]
7
8// This creates an independent copy — modifying slice does not affect numbers
9slice[0] = 99;
10Console.WriteLine(numbers[2]);  // 30 — unchanged

Array.Copy is available in all .NET versions and creates an independent copy.

LINQ Skip and Take

csharp
1using System.Linq;
2
3int[] numbers = { 10, 20, 30, 40, 50, 60, 70 };
4
5// Skip 2 elements, take 3
6int[] slice = numbers.Skip(2).Take(3).ToArray();
7// Result: [30, 40, 50]
8
9// First N elements
10int[] firstThree = numbers.Take(3).ToArray();
11// Result: [10, 20, 30]
12
13// Last N elements
14int[] lastThree = numbers.TakeLast(3).ToArray();
15// Result: [50, 60, 70]
16
17// Skip first and last
18int[] middle = numbers.Skip(1).SkipLast(1).ToArray();
19// Result: [20, 30, 40, 50, 60]
20
21// With filtering
22int[] evenSlice = numbers.Where(n => n > 20).Take(3).ToArray();
23// Result: [30, 40, 50]

LINQ is the most flexible but slowest option due to iterator overhead and allocation.

Comparison Table

MethodCopies DataMin C# VersionPerformanceSyntax
array[1..4]Yes8.0Goodarray[start..end]
Span<T>No7.2Bestarray.AsSpan(offset, length)
ArraySegment<T>No2.0Goodnew ArraySegment(array, offset, count)
Array.CopyYes1.0GoodArray.Copy(src, srcIdx, dst, dstIdx, len)
LINQ Skip/TakeYes3.5Slowarray.Skip(n).Take(m).ToArray()

Common Pitfalls

  • Range end index is exclusive: numbers[1..4] returns elements at indices 1, 2, 3 — not 1, 2, 3, 4. This matches Python and most other languages but can surprise developers expecting an inclusive range.
  • Range operator creates a copy: numbers[1..4] allocates a new array. For performance-critical code, use Span<T> which provides a zero-copy view. The range operator on Span<T> does not allocate.
  • Span cannot be used across await: Span<T> is a ref struct and cannot be stored in async method state machines. Use Memory<T> or ArraySegment<T> when you need to pass array slices across await boundaries.
  • ArraySegment modifications affect the original array: Since ArraySegment is a view, writing to it changes the source array. If you need an independent copy, use the range operator or Array.Copy.
  • Index out of range with ^n: numbers[^0] throws IndexOutOfRangeException because ^0 equals numbers.Length. The last valid index is ^1. Similarly, numbers[^8..] on a 7-element array throws because the start index is before the array.

Summary

  • Use array[1..4] (C# 8.0+) for the most readable range selection
  • Use ^n for end-relative indexing — ^1 is the last element, ^2 is second to last
  • Use Span<T> for zero-copy, high-performance slicing
  • Use ArraySegment<T> when you need a view that works across await boundaries
  • Use Array.Copy for an independent copy in older .NET versions
  • Range end indices are exclusive — [1..4] returns elements at indices 1, 2, 3

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.