Vaughan Reid's blog

Using the new Index and range operators

A great C# feature that was recently released is the new Index and Range operators. Two new system objects were created to make accessing elements or range of elements in arrays shorter and more convenient.

Index

You can now index an array from the end of the array instead of using Array.Length. To be honest, its a little confusing at first because of the fact that arrays are zero indexed making the last element Length - 1 ie: The last element in an array can now be referenced with [^1]. You can pretty much substitute ^ with array.Length in your head if its easier for you. You still start at [0] from the beginning but you start at [^1] at the end.

Breaking up a string into a character array, these are the various indexes.

var input = new char[]
{
	//i frm start - i frm end
	'U',    //0  -  ^8
	's',    //1  -  ^7
	'e',    //2  -  ^6
	'r',    //3  -  ^5
	'A',    //4  -  ^4
	'B',    //5  -  ^3
	'C',    //6  -  ^2
	'D',    //7  -  ^1
};

The two methods below are the same.


[Test]
public void Fourth_from_end()
{
	string input = "UserABCD";

	Assert.AreEqual('A', input[input.Length - 4]);
}

[Test]
public void Fourth_from_end_short()
{
	string input = "UserABCD";

	Assert.AreEqual('A', input[^4]);
}

In the example, ^4 is actually a shorthand for an instance of the struct System.Index which is pointing to 4 elements from the end of an Array.

Range

Ranges are a way to select a range of elements in an array by specifying the start index and the end index. One important thing to note is that ranges don’t include the last element in the range (they are exclusive). ie: 0..1 is only the first character.

As an example, the next two methods will both pass.

	[Test]
	public void Last_four_characters()
	{
		string input = "UserABCD";

		Assert.AreEqual("ABCD", input.Substring(input.Length - 4));
	}

	[Test]
	public void Last_four_characters_short()
	{
		string input = "UserABCD";

		Assert.AreEqual("ABCD", input[^4..]);
		Assert.AreEqual("AB", input[^4..^2]);
	}
	

As you can see, the last part of the range is optional. It will go to the end if not specified. The way I read input[^4..^2] is a range from 4 elements from the end to 2 elements to the end.

One thing to note if performance is important to your application is that a range will create a new string or array as the result. For small strings this doesn’t matter but there are cases where large strings will start allocating too much memory. For those cases, you can still use Range with Span and Memory and it will be equivalent to the Slice operations.


	[Test]
	public void Last_four_characters_memory()
	{
		string input = "UserABCD";

		var output = input.AsMemory()[^4..^0];

		Assert.AreEqual("ABCD", output.ToString());
	}
	[Test]
	public void Last_four_characters_span()
	{
		string input = "UserABCD";

		var output = input.AsSpan()[^4..^0];

		Assert.AreEqual("ABCD", output.ToString());
	}