Zie Scherp Scherper - Hoofdstuk 16
Na dit hoofdstuk kan je:
is- en as-keywords en pattern matching gebruikenEquals netjes overridenPolymorfisme = poly (meerdere) + morfisme (vormen): meerdere vormen.
Het laat toe om child-objecten te behandelen als objecten van hun parent-klasse, en zorgt dat
virtual/overrideecht werkt.
Ik ben Tim. Maar ook: een Docent is een Werknemer, en een Werknemer is een Mens.
Steeds dezelfde Tim, maar in welke rol je me ziet, hangt af van de context. Dat is polymorfisme.
Waarom voert a.MaakGeluid() toch de Varken-versie uit?
override draait, op basis van het echte object in de heapvirtual of abstract isEen child toewijzen aan een parent-variabele (upcasting) gaat automatisch, tot zelfs:
Belangrijk
De compiler kijkt naar het type van de variabele, niet naar het echte object. Via mijnObject kan je dus enkel wat System.Object kent. Terug naar Varken (downcasting) doe je veilig met is en as.
Een hierarchie kan diep gaan:
Een Dennenboom kan je dus zien als een Naaldboom, een Boom, een Plant of een LevendWezen: upcasting werkt naar elke voorouder.
Een lijst van het basistype, gevuld met child-objecten:
Objecten als één geheel behandelen, zonder dat ze exact hetzelfde type hoeven te zijn.
Polymorfisme werkt enkel mooi als een child zich echt kan gedragen als zijn parent (het Liskov substitution principle).
Het klassieke tegenvoorbeeld: een Vierkant die overerft van Rechthoek klopt taalkundig, maar geeft brokkelige code. Kan elk child overal de plaats van zijn parent innemen?
internal class EersteMinister
{
public MinisterVanMilieu Jansens { get; set; } = new MinisterVanMilieu();
public MinisterBZ Ganzeweel { get; set; } = new MinisterBZ();
public void Regeer()
{
Jansens.VerhoogBosSubsidies();
Jansens.OpenOnderzoek();
Ganzeweel.VervangAmbassadeur();
// ... de premier moet ELK departement van binnen kennen
}
}De EersteMinister weet veel te veel: dat vloekt tegen abstractie.
Elke minister moet Adviseer overriden.
De premier hoeft geen enkel departement nog van binnen te kennen. Wie zei dat regeren moeilijk was?
is test of een object van een bepaald type is, en geeft een bool:
as probeert te converteren; lukt het niet, dan krijg je null in plaats van een crash:
Belangrijk
as werkt enkel op reference types (objecten en interfaces), niet op value types als int.
Tip
Vuistregel: wil je het object daarna gebruiken, kies as; wil je enkel weten of iets van een type is, kies is.
Sinds C# 7 doe je de check én de omzetting in één stap:
Tip
In dezelfde geest: if (jos is not null) is de modernere variant van != null. Je ziet deze vorm overal in moderne C#.
Met is schrijf je een veilige Equals (geen crash bij een verkeerd type):
Belangrijk
Wie Equals overridet, override ook GetHashCode (met HashCode.Combine), anders haperen Dictionary en HashSet.
== vergelijkt bij objecten standaard de referenties: twee aparte objecten met dezelfde inhoud zijn dus niet gelijkEquals te overriden bepaal jij gelijkheid op basis van de inhoudTip
Sinds C# 9 regelen record-types dit automatisch: public record Student(string Voornaam, int Geboortejaar); genereert zelf Equals, GetHashCode én ToString. We doen het hier met de hand om te begrijpen wat eronder zit.
De wapens uit H15 worden nu polymorf: elk type onderhoudt zich anders.
Tip
De Held roept gewoon wapen.Maintain() aan voor elk wapen in zijn handen. Late binding kiest de juiste versie (Zwaard, Boog of Staf die mana verdubbelt).
overrideis en asList<Basistype> met child-objecten maakt code flexibel en kort (de premier!)is toetst een type, as zet veilig om (null bij mislukking), pattern matching doet beideEquals voor vergelijking op inhoud, en vergeet GetHashCode nietTip
Volgende halte: interfaces, contracten die klassen beloven na te komen.
Kennisclips
Dezelfde concepten uit dit hoofdstuk, maar dan in andere talen. Handig om later code in Python of JavaScript te leren lezen.
C# heeft een gemeenschappelijke parent plus virtual/override nodig. Python doet het via duck typing: heeft het object de methode, dan werkt het gewoon, zonder gedeelde basisklasse.
Bij Python merk je pas tijdens het uitvoeren of een object de methode echt heeft; C# controleert dat al bij het compileren.
Dezelfde vraag, andere notatie. In Python is het een functie:
In JavaScript een operator:
Zelf bepalen wanneer objecten gelijk zijn, doe je via een vaste methode. Python override __eq__ (zit achter ==):
Java lijkt op C#: override equals (en hashCode):
In JavaScript kan === niet overschreven worden: daar schrijf je zelf een functie zijnGelijk(a, b).
Een handige samenvattende poster (Opgelet: gemaakt m.b.v. ChatGPT Image Gen 2).