INDIVIRTUAL - TECHNISCH PARTNER IN DIGITALE DIENSTVERLENING

C# 8.0: Ranges en indexen

February 13, 2019

C# 8.0: Ranges en indexen

Deze blog is onderdeel van een serie over nieuwe features in C# 8.0. Onderwerp van deze blog zijn de ranges en indexen die in C# 8.0 toegevoegd worden. Stel, je hebt een functie die een IEnumerable<string> teruggeeft met daarin alle namen uit de lijst met meest gegeven namen uit 2018. Maar eigenlijk wil je enkel de eerste 10 weergeven, hoe ga je dit doen?

Natuurlijk was er altijd al de Take functie in de System.Linq namespace, maar in C# 8.0 komt er ook een andere operator bij. Je kunt namelijk een range opgeven, waardoor je in plaats van deze code:

foreach (var name in names.Take(10))
{
    Console.WriteLine(name);
}

Dit kunt schrijven:

foreach (var name in names[0..11])
{
    Console.WriteLine(name);
}

Wacht, waarom staat er [0..11] als we enkel de eerste 10 wilde ophalen? Dat komt doordat het laaste getal exclusief is, de range loopt dus tot dat item en niet tot en met. Maar is dit nu echt leesbaarder dan een Take? Niet echt he?

Maar een range hoef je niet ter plekke te bepalen, deze kun je ook elders bepalen en opslaan in een Range type. Ook kun je natuurlijk variabelen gebruiken om een Range op te bouwen. Iets als dit is dus volledig valide:

private Range GetRange(int begin, int end)
{
    return [begin..end]
}

Daarnaast kunnen ranges open zijn aan één van beide kanten, dus je kunt een range definieren als [..6] om de eerste 5 items op te halen. Maar dit is nog steeds niet duidelijker dan een Take(5)

De echte trucjes komen pas zodra je ook met de ^ gaat werken. Hiermee geef je aan dat je aan het einde van de lijst wilt beginnen. Op deze manier kun je dus eenvoudig enkel de laatste n items op kunt halen. De syntax hiervoor is someArray[^n..], en dit is wel een stuk netter dan someArray.Reverse().Take(n). Dit werkt ook met het ophalen van een enkel item, dus je kunt nu someArray[^1] doen om het laatste item uit een lijst te halen. Met LINQ kom je daarvoor uit op een query als someArray.Last().

Natuurlijk is dit niet beperkt tot enkel lijsten, ook een string ondersteunt dit. Je kunt het dus ook gebruiken als een alternatief op someString.Substring(startIndex, length). Het returntype hiervan is gewoon een string. Het werkt voor alles dat een indexer definieert die het type Range accepteert. Wil je dit voor je eigen type maken dan moet je een property toevoegen public someObj[] this[Range range] { get { /* ... */ } }. Een simpele implementatie zou je als volgt kunnen schrijven:

public class ListWithRange<T> : List<T>
{
    public List<T> this[Range range]
    {
        get
        {
            var startAt = range.Start.FromEnd ? this.Count - range.Start.Value : range.Start.Value;
            var endAt = range.End.FromEnd ? this.Count - range.End.Value : range.End.Value;
            return this.Skip(startAt).Take(endAt - startAt);
        }
    }
}

Kort samengevat:

  • Ranges zijn inclusief op het begin, exclusief op het einde (dus someArray[1..11] haalt maar 10 items op)
  • Eén van de twee getallen mag je weglaten, dan wordt er ofwel vanaf het begin ofwel tot het einde opgehaald
  • Door een ^ voor het getal te zetten kun je vanaf het einde tellen
  • Je kunt een Range object maken om je range in op te slaan
  • Het werkt niet enkel op lijsten, ook op string
  • Het is eenvoudig zelf te implementeren door middel van een indexer property

Zorg dat je ons volgt op Twitter, Facebook of LinkedIn voor meer nieuwe features in C# 8.0!

Sanne Bregman

Sanne Bregman

.NET Developer