Zie Scherp Scherper - Hoofdstuk 14
Na dit hoofdstuk kan je:
uitleggen dat alles erft van System.Object
de ingebouwde methoden ToString, Equals en GetHashCode overriden
een abstracte klasse en abstracte methoden maken
het verschil tussen abstract en virtual/sealed uitleggen
een eigen exception ontwerpen en opwerpen
Volgens Darwin stamt alle leven af van een gemeenschappelijke voorouder. En voor de niet-darwinisten onder ons: in den beginne schiep .NET System.Object.
Hoe je het ook bekijkt: elke klasse in C# stamt af van System.Object.
Het is de stamvader van alles.
Elke klasse heeft er meteen vier, van System.Object:
ToString()
een string die het object voorstelt
Equals()
zijn twee objecten gelijk?
GetHashCode()
unieke hash van het object
GetType()
het datatype van het object
Opmerking
Drie ervan zijn virtual (ToString, Equals, GetHashCode): die kan je overriden. GetType() niet: dat moet altijd het echte type teruggeven.
GetType() geeft een Type-object terug, met o.a. een Name-property.
Console.WriteLine(stud1) roept stiekem stud1.ToString() aan. Standaard geeft dat de klassenaam. Override het voor iets nuttigs:
Jij bepaalt wanneer twee objecten “gelijk” zijn.
Opmerking
o is not Student temp checkt het type én steekt o meteen als Student in temp. Dat is-patroon zie je voluit in hoofdstuk 18.
Override je Equals, override dan ook GetHashCode.
gelijke objecten moeten dezelfde hash geven, belangrijk voor Dictionary en HashSet
Tip
Geef dezelfde properties mee die je in Equals vergelijkt. HashCode.Combine doet het rekenwerk voor je.
System.Object geeft ook MemberwiseClone(): een shallow copy (ondiepe kopie).
Shallow
instantievariabelen van een valuetype worden gekopieerd, maar geneste objecten worden gedeeld (enkel de referentie gaat mee)
Deep
ook de geneste objecten zelf kopiëren, dat vraagt handwerk
Waarschuwing
Pas je in de kopie een gedeeld nested object aan, dan verandert het origineel mee. Voor een echte, losse kopie heb je een deep copy nodig.
“Ok, wow? Vier methoden en wat beloofde compatibiliteit? Call me unimpressed.”
Begrijpelijk. We leggen puzzelstukjes klaar die straks samenkomen. Polymorfisme wordt onze doelpuntenmaker, en System.Object geeft telkens de perfecte voorzet.
Geef mij de blauwdruk van een “meubel”. Een tafel? Een kast? Een bed?
Sommige concepten zijn te abstract om er een object van te maken.
Je kan niet new Dier() doen
Je kan er wel van overerven: Paard : Dier, Wolf : Dier
Tip
abstract en sealed zijn tegenpolen: bij abstract moet je overerven, bij sealed mag het net niet.
Welk geluid maakt “een dier”? Onbekend. Geef enkel de signatuur:
Child-klassen moeten dit overriden:
Belangrijk
virtual = override mag, abstract = override moet. Een abstracte methode kan enkel in een abstracte klasse.
Ook properties kunnen abstract; de child vult de implementatie in:
Een gemeenschappelijke SpelObject voor alles op het scherm, zonder te weten hoe het getekend wordt:
Balletje
erft hiervan en implementeert zijn eigen TekenOpScherm()
een nieuw ScoreBoard
erft hiervan en implementeert zijn eigen TekenOpScherm()
Exceptions zijn ook maar klassen die erven van Exception.
Werp er zelf een op met throw:
Elders vang je die op met catch (DivideByZeroException e).
Erf van Exception en voorzie de drie standaard-constructors:
Waarschuwing
Alles erft van System.Object, met o.a. ToString, Equals, GetHashCode (allemaal virtual)
Override ToString voor leesbare objecten; override Equals + GetHashCode samen
Een abstract klasse kan je niet instantiëren, maar dient als basis voor children
Een abstracte methode zonder body moet de child overriden
Eigen exceptions erven van Exception; gebruik throw; om de stack trace te bewaren
Tip
Volgende halte: compositie en aggregatie: de “heeft-een”-relatie.
Kennisclips
Hoe doen andere programmeertalen hetzelfde? En een korte zoek-de-fout, om zo kritisch mogelijk naar code te leren kijken.
Onderstaande C#-code wil twee studenten vergelijken op hun naam. De Equals-override doet z’n werk, maar bij het gebruik van Student-objecten als sleutel in een Dictionary of HashSet lopen dingen mis. Wat ontbreekt er?
Wie Equals override, moet ook GetHashCode override. Twee gelijke objecten horen dezelfde hashcode terug te geven, en daar rekenen Dictionary en HashSet op.
Ook Java kent dezelfde oer-klasse, daar gewoon Object genoemd. Elke klasse erft er impliciet van, met methoden als toString(), equals() en hashCode():
Heel gelijkaardig dus. Ook Python doet dit met object.
C++ kent het keyword abstract niet, maar wel exact hetzelfde idee: een “pure virtual” methode. Je zet = 0 achter de signatuur, en zo’n klasse kan je niet instantiëren:
Python doet dit met de ABC-module en @abstractmethod.
In Python werkt dit verrassend gelijkaardig: erf over van Exception en gooi op met raise (het equivalent van throw):
Een handige samenvattende poster (Opgelet: gemaakt m.b.v. ChatGPT Image Gen 2).