In C# 8.0, use ^ to count an element’s position from the end of a sequence and .. to describe a contiguous slice. For example, numbers[^1] selects the last element, while numbers[1..4] selects elements at offsets 1, 2 and 3. The range’s start is included; its end is excluded.
What the ^ and .. operators mean
The operators are concise syntax for the System.Index and System.Range types. Microsoft Learn describes them as a way to access “single elements or ranges in a sequence” with succinct syntax (Microsoft Learn: Explore ranges of data using indices and ranges).
^ndescribes an index counting backward from the end. For a sequence of lengthN, it corresponds to offsetN - n.start..enddescribes a range that includesstartand excludesend.- In a range, an omitted start means the beginning; an omitted end means the position just past the final element.
For a sequence of five elements, ^1 maps to offset 4, the last element; ^5 maps to offset 0, the first. ^0 maps to offset 5, the past-end position. That makes ^0 useful as a range endpoint, but not as an element to read.
Use an index to select one element
Use an ordinary integer index when the offset from the start is clearest. Use ^ when the position is naturally described from the end:
Recommended Free Tools
#1 Best Overall
int[] numbers = { 10, 20, 30, 40, 50 };
int first = numbers[0]; // 10
int last = numbers[^1]; // 50
int secondLast = numbers[^2]; // 40
Thus, numbers[^1] is equivalent to numbers[numbers.Length - 1] for this array. The index-from-end notation does not make an invalid position valid: numbers[^0] attempts to read at the past-end offset and is out of bounds, just like numbers[numbers.Length].
Use a range to select a contiguous slice
A range’s right endpoint is exclusive, so 1..4 selects offsets 1, 2 and 3—not offset 4. The range length is the concrete end offset minus the concrete start offset.
int[] numbers = { 10, 20, 30, 40, 50 };
int[] middle = numbers[1..4]; // 20, 30, 40
int[] inner = numbers[1..^1]; // 20, 30, 40
int[] all = numbers[..]; // 10, 20, 30, 40, 50
int[] withoutLast = numbers[..^1]; // 10, 20, 30, 40
int[] fromThird = numbers[2..]; // 30, 40, 50
These examples use an array, for which slicing with range syntax produces an array. On other types, check the target type’s indexer or slicing API rather than assuming the result has the same type or behavior.
Understand empty ranges
A valid range can select no elements. For example, numbers[2..2] is an empty slice. This is different from reading one element: element access requires an index that identifies an existing element, while a slice describes a start and an exclusive end. An empty sequence has no valid element to read, though a valid empty range may still describe a zero-length slice.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsRank #3
Which types support indices and ranges?
Microsoft documents range and index use with arrays, strings, Span<T> and ReadOnlySpan<T>; its collections overview also discusses arrays and List<T>. Support depends on the members exposed by the particular type. The C# language specification describes how suitable user-defined indexers or sequence-pattern members can make compatible types work with the syntax (C# language specification: Indices and ranges; Microsoft Learn: Common collection types in C#).
For an array, string or span, use the documented index or range form for that type. For a custom sequence, verify that it provides an appropriate Index or Range indexer, or the members needed by the language’s sequence pattern. Also check what the slice operation returns: C# syntax does not impose one universal result type or allocation behavior for every target.
Rank #4
Multidimensional arrays do not support ^ and .. directly. Use the array’s dimensional indexing and the APIs appropriate to the operation instead.
Keep ranges within the sequence bounds
The same range can only be interpreted in relation to a sequence length. When a range is applied, its endpoints must describe a valid region for that length. Invalid endpoints or reversed bounds can cause ArgumentOutOfRangeException. The Range.GetOffsetAndLength(length) method performs this conversion for a supplied length and throws when the range is invalid for it.
Best Value
For example, with a sequence of length five, 1..4 is valid, but an endpoint beyond the sequence length is not. A reversed range such as 4..1 does not mean “walk backward”; ranges describe forward contiguous regions.
Choose the clearest form for the job
- One known offset: use an integer index such as
items[2]. - One element relative to the end: use
items[^1]for the last element or another positive distance as appropriate. - A contiguous region: use a range such as
items[2..5], and explain that the endpoint is excluded. - A custom collection: confirm its index/range support, valid bounds, and slice return type in that collection’s API.
The syntax expresses positions and boundaries; it does not by itself guarantee a particular performance or allocation cost for every collection. Consult the target type’s documentation when those characteristics matter.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

