C# 8 is Introducing Ranges

We have been talking a lot about C# 8 features recently. We have talked about target-typed new expressions, about default interface methods and even about minor features like fixing interpolated verbatim strings. But one feature that we discussed before is the introduction of indexes. That feature will go hand in hand with another feature, the introduction of ranges.

C# language has no syntactic way to access ranges or slices of any part of a collection. Developers are usually forced to perform some custom slice management or resort to LINQ methods like array.Skip(5).Take(2);.

Now C# is adding a new small feature, that can help people in getting a range of items from a bigger array of items.

Let’s check the basics. Let’s say we had an array of items, which we just needed to log only some of them. Normally we would just loop through some and log them like the following.

var array = new string[] 
    {
        "Item1",
        "Item2",
        "Item3",
        "Item4",
        "Item4",
        "Item5",
        "Item6",
        "Item7",
        "Item8", 
        "Item9"
    };

    for(int i=1; i <= 5; i++)
    {
        Console.WriteLine(array[i]);
    }

So we wanted only a few specific ones, and we got them by controlling our index value.

By using ranges, we could do the following instead

var array = new string[] 
    {
        "Item1",
        "Item2",
        "Item3",
        "Item4",
        "Item4",
        "Item5",
        "Item6",
        "Item7",
        "Item8", 
        "Item9"
    };

    foreach (var item in array[1..5])
    {
        Console.WriteLine(item);
    }

Now here, first timers would notice something peculiar, it would only print four items.
Why is that? Well the start of an index is inclusive, but the range is exclusive
If we wanted the same effect as the loop version our foreach loop should be

    foreach (var item in array[1..6])
    {
        Console.WriteLine(item);
    }

Shortcut Ranges

1. From An Index (Inclusive) To The End (Inclusive)

    foreach (var item in array[1..])
    {
        Console.WriteLine(item);
    }

2. From The Start (Inclusive) To An Index (Exclusive)

    foreach (var item in array[..3])
    {
        Console.WriteLine(item);
    }

3. Combining the two above, you could have, a syntactically correct version of the following

    foreach (var item in array[..])
    {
        Console.WriteLine(item);
    }

which basically means, give me the whole range.

4. From Index (Inclusive) To X From The End (Exclusive)

    foreach (var item in array[1..^1])
    {
        Console.WriteLine(item);
    }

Now this combines the hat operator of the index which explained in a previous article

But a quick explanation, the hat operator, gives you a specific index. If you write ^1 you are basically requesting the index of the last item. And given that, as explained above, putting a last index number is exclusive, in a sequence of 9 items, requesting all of them up to ^1, you are basically requesting all of them up to 9, and that item will not be included.
I hope I was clear.

Range as a type

What we can do now, is use Ranges as a type to pass around. Which is what we did earlier, but we can also store it in a variable or pass it around, by simply typing

Range range = 1..9;

Pass it as arguments in methods, store as a private variable, anything that your business logic requires.

Now, ranges, can not be used by List or IEnumerable<Τ> but array is not the only type that supports them.

You also can use indices and ranges with string, Span, or ReadOnlySpan

An example with strings is basically a replacement of the Substring method like the following.

string s = "1234567";
string r = s[1..3]; // r will now be holding "12"

Now to everyone worrying about their lists (which probably is what you usually use instead of array), List already has the GetRange method since .Net Framework 2.0 (which is why they went with a method here).

Please follow and like us:

Leave a Reply