List collectie
Een List<>-collectie is de meest standaard collectie die je kan beschouwen als een veiligere variant op een doodnormale array. Een List heeft alle eigenschappen die we al kennen van arrays, maar ze zijn wel krachtiger. Het giet een klasse “rond” het concept van de array, waardoor je toegang krijgt tot een hoop nuttige methoden die het werken met arrays vereenvoudigen.
List aanmaken
De klasse List<> is een generieke klasse. Tussen de < >tekens plaatsen we het datatype dat de lijst zal moeten gaan bevatten. Bijvoorbeeld:
List<int> alleGetallen = new List<int>();
List<bool> binaryList = new List<bool>();
List<Pokemon> pokeDex = new List<Pokemon>();
List<string[]> listOfStringarrays = new List<string[]>();Zoals je ziet hoeven we bij het aanmaken van een List geen begingrootte mee te geven, wat we wel bij arrays moeten doen. Dit is één van de voordelen van List: ze groeien mee.

In dit boek behandel ik het concept generieke klassen enkel in de appendix.
Generieke klassen oftewel generic classes zijn een handig concept om je klassen nog multifunctioneler te maken doordat we zullen toelaten dat bepaalde datatypes niet hardcoded in onze klasse moet gezet worden. List<> is zo’n eerste voorbeeld, maar er zijn er tal van anderen én je kan ook zelf dergelijke klassen schrijven. Bekijk zeker de appendix indien je dit interesseert.
De generieke List<> klasse bevindt zich in de System.Collections.Generic namespace. Je dient deze namespace dus als using bovenaan toe te voegen wil je deze klasse kunnen gebruiken in C# 9.0 en ouder.
Elementen toevoegen
Via de Add()-methode kan je elementen toevoegen aan de lijst. Je dient als parameter aan de methode mee te geven wat je aan de lijst wenst toe te voegen. Deze parameter moet uiteraard van het type zijn dat de List verwacht.
In volgende voorbeeld maken we een List aan die objecten van het type string mag bevatten en vervolgens plaatsen we er twee elementen in.
List<string> mijnPersonages = new List<string>();
mijnPersonages.Add("Reinhardt");
mijnPersonages.Add("Mercy");Ook meer complexe datatypes kan je dus toevoegen:
List<Pokemon> pokedex = new List<Pokemon>();
pokedex.Add(new Pokemon());Via object syntax initializer kan dit zelfs nog sneller:
List<Pokemon> pokedex = new List<Pokemon>()
{
new Pokemon(),
new Pokemon()
};Je kan ook een stap verder gaan en ook binnenin deze initializer syntax dezelfde soort initialize syntax gebruiken om de objecten individueel aan te maken:
List<Pokemon> pokedex = new List<Pokemon>()
{
new Pokemon() {Naam = "Pikachu", HP_Base = 5},
new Pokemon() {Naam = "Bulbasaur", HP_Base = 15}
};Elementen indexeren
Het leuke van een List is dat je deze ook kan gebruiken als een gewone array, waarbij je met behulp van de indexer elementen individueel kan aanroepen. Stel bijvoorbeeld dat we een lijst hebben met minstens 4 strings in. Volgende code toont hoe we de string op positie 3 kunnen uitlezen en hoe we die op positie 2 overschrijven, net zoals we reeds kenden van arrays:
Console.WriteLine(mijnPersonages[3]);
mijnPersonages[2] = "Torbjorn";Ook de klassieke werking met loops blijft gelden. De enige aanpassing is dat List<> niet met Length werkt maar met Count:
for(int i = 0 ; i < mijnPersonages.Count; i++)
{
Console.WriteLine(mijnPersonages[i])
}Wat kan een List nog?
Interessante methoden en properties voorts zijn:
Clear(): methode die de volledige lijst leegmaakt en de lengte (Count) terug op 0 zet.Insert(): methode om een element op een specifieke plaats in de lijst in te voegen.IndexOf(): geeft de index terug van het element item in de rij. Indien deze niet in de lijst aanwezig is dan wordt -1 teruggegeven.RemoveAt(): verwijdert een element op de index die je als parameter meegeeft.Sort(): alle elementen in de lijst worden gesorteerd. Voor lijsten van ingebouwde types (int,string, enz.) werkt dit meteen. Voor een lijst van je eigen klassen weet C# echter niet hoe het ze moet rangschikken: je krijgt dan eenInvalidOperationException. Dat los je pas op door je klasseIComparablete laten implementeren. Lees daarvoor in hoofdstuk 17 de sectie “Interfaces in de praktijk”.
Let op met het gebruik van IndexOf (en Contains) en objecten. Deze methoden controleren of de referentie dezelfde is van een bepaald object. Je vindt je gewenste object dus enkel terug indien je reeds een referentie naar dat exacte object hebt en dit meegeeft als parameter. Twee aparte objecten met dezelfde inhoud worden dus als verschillend beschouwd (tenzij je later Equals overridet, zie hoofdstuk 13).
Een wereld met OOP: Pong list
Ikzelf ben fan van List. Het maakt je code vaak leesbaarder dan arrays. Voorts geeft het je de optie om dynamisch groeiende (en krimpende) arrays te hebben, zonder dat je daar veel boilerplate code voor moet schrijven. Herinner je onze Pong-code waarin we 100 balletjes op het scherm lieten vliegen?
const int AANTAL_BALLETJES = 100;
Random r = new Random();
Balletje[] veelBalletjes = new Balletje[AANTAL_BALLETJES];
for (int i = 0; i < veelBalletjes.Length; i++) //balletjes aanmaken
{
veelBalletjes[i] = new Balletje();
veelBalletjes[i].X = r.Next(10, 20);
veelBalletjes[i].Y = r.Next(10, 20);
veelBalletjes[i].VX = r.Next(-2, 3);
veelBalletjes[i].VY = r.Next(-2, 3);
}
while (true)
{
for (int i = 0; i < veelBalletjes.Length; i++)
{
veelBalletjes[i].Update(); //update alle balletjes
}
for (int i = 0; i < veelBalletjes.Length; i++)
{
veelBalletjes[i].TekenOpScherm(); //teken alle balletjes
}
System.Threading.Thread.Sleep(50);
Console.Clear();
}Vooral de code in de while wordt nu leesbaarder dankzij List<Balletje> (we gaan ook ineens gebruik maken van onze nieuwe default constructor die de random startwaarde instelde):
const int AANTAL_BALLETJES = 100;
List<Balletje> veelBalletjes = new List<Balletje>();
for (int i = 0; i < AANTAL_BALLETJES; i++) //balletjes aanmaken
{
veelBalletjes.Add(new Balletje());
}
while (true)
{
foreach(var bal in veelBalletjes)
{
bal.Update(); //update alle balletjes
}
foreach(var bal in veelBalletjes)
{
bal.TekenOpScherm(); //teken alle balletjes
}
System.Threading.Thread.Sleep(50);
Console.Clear();
}Deze code zou je aan iemand die geen C# kan kunnen tonen en met een beetje geluk zal die de code begrijpen. Dat is het fijne van hogere programmeertalen zoals C#: ze zijn veel leesbaarder dan talen die dichter tegen het metaal zitten, zoals C en C++.
Als leuke extra bij C# is dat het een erg levende taal is. Jaarlijks komen er nog nieuwe concepten bij. Meestal zijn die ietwat obscuur, maar vaak maken ze de code wel een pak leesbaarder dan ervoor. Alhoewel ik graag werk met arrays, zorgen Lists er bijvoorbeeld voor dat we veel minder met vierkante haakjes moeten werken én verstoppen ze een hoop code om bijvoorbeeld lijsten te doen groeien en krimpen.
Dit verstoppen kan uiteraard soms een probleem zijn indien je hoog-performante code moet schrijven. Aan de andere kant: denk je dat jij betere code kunt schrijven dan de ontwikkelaars van de Add-methode bij List?
LINQ behandelen we niet in dit handboek, maar weet dat dit een heel sterke .NET technologie is waarmee je in één leesbare regel een lijst kunt filteren, sorteren of omvormen (bijvoorbeeld “geef alle Pokémon met meer dan 100 HP”). Voorlopig doen we dat nog met loops, maar weet alvast dat het straks veel korter kan.
Stagiair Steven
Steven moet alle balletjes verwijderen die buiten het scherm vliegen. De A.I. stelde deze
foreachvoor en hij plakt ze erin:foreach (Balletje bal in veelBalletjes) { if (bal.X < 0) { veelBalletjes.Remove(bal); } }“Ik loop gewoon over de lijst en gooi eruit wat weg moet”, zegt hij.
Deze code compileert. Toch crasht ze zodra er echt een balletje verwijderd wordt. Waarom?
Je mag een lijst niet wijzigen terwijl je er met een foreach overheen loopt. Zodra Steven Remove aanroept verandert de lijst onder de foreach, en C# gooit een InvalidOperationException: “Collection was modified; enumeration operation may not execute.” Wil je tijdens het doorlopen elementen verwijderen, gebruik dan een for-lus die van achter naar voor telt, of verzamel eerst wat weg moet en verwijder het pas daarna. De code zag er voor Steven logisch uit, en omdat ze compileert vertrouwde hij erop tot ze in zijn gezicht ontplofte.
Steven moet alle balletjes verwijderen die buiten het scherm vliegen. De A.I. stelde deze