DEV Community

Cover image for C# Ranges and Indexes
Andrei Fedotov
Andrei Fedotov

Posted on

C# Ranges and Indexes

Intro

In this tutorial, we're going to go through two new .NET types - System.Index, System.Range and cover appropriate operators: ^ and ...

If you are interested in learning more about C# language, this is for you.

You’ll be able to use the skills and lessons in this tutorial in the real dev world when you work on any project using C#.

It will provide you with a more elegant way to work with sequences and laconic syntax to access elements. Long story short - to use modern language constructions to write easily readable and understandable code.

Ranges and Indexes were introduced as part of .NET Standard 2.1. This is C# 8 feature. It is available in .NET 5, netcore 3.0. It is not supported by .NET Framework. It can be applied for arrays, spans, and readonly spans.

Preparations

First, go to your favorite IDE, it might be Visual Studio, VS Code, Rider, LINQPad, etc. Make sure to start with nothing open or running (except your computer!) I prefer Visual Studio

Next, create a new project and go through the steps.

Steps

The dataset I’m going to use for this tutorial is just a string array of languages.

Create a collection. Let's create an array with the languages.

var languages = new string[] { "Spanish", "English", "Japanese", "French", "German", "Russian", "Italian", "Korean", "Chinese" };
Enter fullscreen mode Exit fullscreen mode
Index

How can we get the second element - "English"? It is pretty easy to do, we just have to pass the index (remember that the numeration if indexes in collections in C# starts from 0)

int index = 1;
var english = languages[index];
Console.WriteLine(english); // Output: English
Enter fullscreen mode Exit fullscreen mode

Instead of int variable we can use Index type for doing the same:

Index index1 = 1;
var english2 = languages[index1];
Console.WriteLine(english2);
Enter fullscreen mode Exit fullscreen mode

Here is another way to create index. Index is value type so when we do new Index() it will set the default value to 0

var index2 = new Index(); // zero by default because this is a structure
var spanish = languages[index2];
Console.WriteLine(spanish);
Enter fullscreen mode Exit fullscreen mode

We can pass the index value as a parameter to the constructor

var index3 = new Index(2);
var japanese = languages[index3];
Console.WriteLine(japanese);
Enter fullscreen mode Exit fullscreen mode

Probably for now you might ask what is the benefit to use index rather than just integer variable? Okay, probably for accessing the first or second elements there are no many benefits. But in the next examples, we will see getting the last element and will see much more benefits when we will try indexes with ranges together.

The second question here is how can we get the last element? Okay, we can simply get the length of the array and subtract 1. (because of the first element has the index 0 that's way the last element has the index last element - 1)

var lastIndex1 = languages.Length - 1;
var chinese1 = languages[lastIndex1];
Console.WriteLine(chinese1);
Enter fullscreen mode Exit fullscreen mode

And the same by using Index:

Index lastIndex2 = ^1;
var chinese2 = languages[lastIndex2];
Console.WriteLine(chinese2);
Enter fullscreen mode Exit fullscreen mode

We can do both above examples in one line.

Console.WriteLine(languages[languages.Length - 2]); // Korean
Enter fullscreen mode Exit fullscreen mode

vs

Console.WriteLine(languages[^2]); // Korean
Enter fullscreen mode Exit fullscreen mode

The struct Index has overwritten method Equals so we can check the equality of the two indexes.

var equals = index1.Equals(index2);
Console.WriteLine(equals); // false
Enter fullscreen mode Exit fullscreen mode
Range

Usually, users are forced to implement complex structures to filter/operate on slices of memory or resort to LINQ methods like list.Skip(5).Take(2). With the addition of System.Span<T> and other similar types, it becomes more important to have this kind of operation supported on a deeper level in the language/runtime and have the interface unified.

Range operator .. is a binary infix operator that accepts two expressions, or both expressions can be omitted.

Range myRange1 = 1..4;
Enter fullscreen mode Exit fullscreen mode

This can be used with indexes together:

Index start = 1;
Index end = 4;
Range myRange2 = start..end;
Enter fullscreen mode Exit fullscreen mode

The following will output the 1, 2 and 3 elements from array:

string[] languagesRange = languages[1..4];
foreach (var language in languagesRange)
{
    Console.WriteLine(language);
}
Enter fullscreen mode Exit fullscreen mode

A bit more interesting usages.

3 last elements:

string[] languagesRange1 = languages[^3..];
Enter fullscreen mode Exit fullscreen mode

From start to pre-last element (every except the last one)

string[] languagesRange2 = languages[..^1];
Enter fullscreen mode Exit fullscreen mode

3 elements before pre-last element

string[] languagesRange3 = languages[^4..^1];
Enter fullscreen mode Exit fullscreen mode

As I mentioned earlier the same we can do with Span

Span<string> languagesSpan = languages;
Span<string> selectedLanguagesSpan = languagesSpan[1..4];
foreach (var language in selectedLanguagesSpan)
{
    Console.WriteLine(language);
}
Enter fullscreen mode Exit fullscreen mode

Thank you

Thank you very much for reading. I hope it brought something new to you. Hope you'll find a chance to give it a try for these features in your projects. Also, I would appreciate any feedback from you. Feel free to post your comments below.

The source code can be found here.

Top comments (2)

Collapse
 
bigboybamo profile image
Olabamiji Oyetubo

Didnt know about indexes until now, great article, Andrei

Collapse
 
andreisfedotov profile image
Andrei Fedotov

Thank you for your feedback, Bamiji!