📏 arr[^1] is Last Element. arr[1..3] is Range.
arr[arr.Length – 1] is verbose. ^ operator indexes from end. .. operator creates ranges. Python-like slicing in C#.
❌ Old Way
var last = arr[arr.Length - 1]; var secondLast = arr[arr.Length - 2]; var slice = arr.Skip(1).Take(3).ToArray();
✅ Index and Range
var last = arr[^1]; var secondLast = arr[^2]; var slice = arr[1..4];
🎯 Range Patterns
int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
arr[^1] // 9 (last)
arr[^3] // 7 (third from end)
arr[2..5] // { 2, 3, 4 }
arr[..5] // { 0, 1, 2, 3, 4 } (from start to index 5)
arr[5..] // { 5, 6, 7, 8, 9 } (from index 5 to end)
arr[^5..^2] // { 5, 6, 7 } (from 5th from end to 2nd from end)
arr[..] // entire array copy
// Work with strings
string s = "Hello World";
s[^1] // 'd'
s[6..11] // "World"
s[..5] // "Hello"
// Work with Span and ReadOnlySpan
Span<int> span = arr;
var slice = span[1..4];
💡 Benefits
- Cleaner, more readable code
- No off-by-one errors with length calculation
- Works with arrays, strings, spans, and custom types with Count
- Range returns a copy (for spans, it’s a view)
“arr[^1] is so much better than arr[arr.Length – 1]. Ranges eliminate manual slicing. Python devs will feel at home in C# now.”
