5Polymorfisme
Veelvormigheid: dezelfde aanroep, maar elk object voert zijn eigen versie uit.
| Overloading | Overriding | |
|---|---|---|
| Wat | Zelfde naam, andere parameters, in dezelfde klasse | Subklasse geeft eigen implementatie van ouder-methode |
| Wanneer bepaald | Compile-time | Runtime (late binding / dynamic dispatch) |
| Keywords | geen | virtual/abstract + override |
| Voorbeeld | Tel(int a, int b) en Tel(int a, int b, int c) | override decimal BerekenPrijs(int m) |
#Polymorfe collectie
List<Voertuig> lijst = new() { new Fiets("F1", "Gazelle", 7), new Scooter("S1", "Niu", "DX-123-B", 80), }; foreach (Voertuig v in lijst) { // juiste override wordt automatisch gekozen Console.WriteLine($"{v.Id}: {v.BerekenPrijs(20):0.00}"); Console.WriteLine(v); // roept overridden ToString() aan }
Uitleg
Eén lijst van het type Voertuig met verschillende soorten erin. De foreach roept steeds dezelfde methode aan, maar elk object voert zijn eigen versie uit. Dat is polymorfisme: nieuwe voertuigtypes werken zonder deze code aan te passen.
Functies en keywords
$"...{x}"- String-interpolatie: zet de waarde van
xdirect in de tekst. new()- Target-typed new: het type wordt afgeleid uit de declaratie links.
foreach- Loopt één voor één door alle elementen van een collectie.
List<T>- Lijst die automatisch groeit; toevoegen met
Add, aantal metCount.
#Indexer
class Collectie { private int[] _arr = new int[10]; public int this[int i] { get => _arr[i]; set => _arr[i] = value; } } var c = new Collectie(); c[0] = 5;
Uitleg
Met een indexer (this[int i]) kun je een eigen klasse gebruiken alsof het een array is. Handig als je de opslag verborgen wilt houden of bij elke toegang wilt controleren.
Functies en keywords
this[...]- Indexer: maakt
obj[i]mogelijk op een eigen klasse. private- Alleen zichtbaar binnen deze klasse.
value- In een setter: de waarde die iemand probeert toe te kennen.
=>- Korte schrijfwijze voor een methode of property die één expressie teruggeeft; in LINQ ook de pijl van een lambda.
var- De compiler leidt het type zelf af uit de rechterkant.
#Upcasting en downcasting
// UPCASTING: kind → ouder/interface. Altijd veilig, impliciet. Hond hond = new Hond("Rex"); Dier dier = hond; // geen cast nodig IVerzorgbaar z = hond; // via interface dier.MaakGeluid(); // "Woef!" (dynamic dispatch) // dier.Verzorg(); // FOUT: Dier kent Verzorg niet // DOWNCASTING: ouder → kind. Expliciet, kan mislukken. Hond h1 = (Hond)dier; // InvalidCastException als het geen Hond is // Veilig optie 1: is-pattern if (dier is Hond h2) h2.Verzorg(); // Veilig optie 2: as (geeft null bij mislukking) Hond? h3 = dier as Hond; h3?.Verzorg(); // Veilig in collecties List<Hond> honden = dieren.OfType<Hond>().ToList();
Uitleg
Upcasting (kind naar ouder of interface) gaat altijd goed en vanzelf; je ziet dan alleen nog wat de ouder kent. Terug naar het kind (downcasting) moet je expliciet doen. Een harde cast crasht als het type niet klopt, is en as controleren eerst en zijn dus veilig.
Functies en keywords
as- Probeert te casten en geeft
nullals het type niet past, in plaats van een exception. ?.- Roept alleen aan als het object niet null is; anders is het resultaat null.
OfType<T>()- LINQ: houdt alleen elementen van type
Tover en cast ze meteen; veilig downcasten. ToList()- Maakt van het LINQ-resultaat een nieuwe
List<T>. (cast)- Harde cast: dwingt het type af en gooit
InvalidCastExceptionals het niet klopt. is- Controleert het type (of een patroon) en kan het object meteen in een nieuwe variabele zetten.
List<T>- Lijst die automatisch groeit; toevoegen met
Add, aantal metCount.
Upcasting in de praktijk
Een private List<Voertuig> naar buiten aanbieden als IReadOnlyList<Voertuig> is upcasting via de abstracte klasse (en beschermt de lijst). OfType<IOplaadbaar>() in een List<IOplaadbaar> is upcasting via de interface.