Zie verder
Even terugblikken
In dit hoofdstuk ontdekte je dat elke klasse in C# uiteindelijk overerft van
System.Object. Daardoor heeft elk object al vier ingebouwde methoden, waarvan er drievirtualzijn en dus door jou aangepast kunnen worden. Daarnaast leerde je abstracte klassen kennen: parent-klassen waarvan je geen instanties kan maken, maar die wel gedeelde code en verplichte “gaten” voor de child-klassen kunnen bevatten.
De kern op een rij:
- Alle klassen stammen af van
System.Object; ook ingebouwde types en zelfsintenbool. ToString(),Equals()enGetHashCode()zijnvirtualen kan je overriden;GetType()niet, want die moet altijd het echte runtime-type teruggeven.- Override je
Equals, dan moet je ookGetHashCodeoverriden (gebruikHashCode.Combine(...)), anders krijg je rare bugs als je het object als sleutel in eenDictionaryofHashSetgebruikt. - Een
abstractklasse kan je niet instantiëren; eenabstractmethode heeft geen body en de child-klasse moet ze overriden. abstractensealedzijn elkaars tegenpolen: bijabstractmoet je overerven, bijsealedmag je net niet overerven.
- Override je
Equalszondernull- of type-controle en cast je hard met(Student)o, dan crasht je code op eennullof een verkeerd type. Gebruikif (o is not Student temp) return false;. - Een abstracte property zoals
public abstract int MaxLeeftijd { get; }is géén auto-property: er zit geen verborgen instantievariabele achter, de child moet de implementatie zelf voorzien.
In andere talen
Één oer-klasse
Dit is geen typisch C#-trucje: ook Java kent precies dezelfde oer-klasse, daar gewoon Object genoemd. Elke klasse erft er impliciet van, en je krijgt er eveneens methoden zoals toString(), equals() en hashCode() bovenop:
class Student {
// erft automatisch van java.lang.Object
}
Student stud = new Student();
System.out.println(stud.getClass().getName()); // StudentHeel gelijkaardig dus: één gemeenschappelijke basisklasse waar alles van afstamt. Ook Python doet dit met object.
Abstract zonder keyword
C++ kent het keyword abstract niet. Toch bestaat exact hetzelfde idee daar onder een andere naam: een “pure virtual” methode. Je zet = 0 achter de signatuur, en zo’n klasse kan je niet instantiëren:
class Dier {
public:
virtual std::string MaakGeluid() = 0; // pure virtual: geen body
};
class Paard : public Dier {
public:
std::string MaakGeluid() override { return "Hinnikhinnik"; }
};Het is hetzelfde concept: een klasse met een gat dat de child moet invullen. Python doet dit met de ABC-module en @abstractmethod, dichter bij de C#-aanpak.
Zoek de fout
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?
internal class Student
{
public string Voornaam { get; set; }
public int Geboortejaar { get; set; }
public override bool Equals(object o)
{
if (o is not Student temp)
return false;
return Geboortejaar == temp.Geboortejaar && Voornaam == temp.Voornaam;
}
}Wie Equals override, moet ook GetHashCode override. Twee gelijke objecten horen dezelfde hashcode terug te geven, en daar rekenen Dictionary en HashSet op. Voeg toe:
public override int GetHashCode()
{
return HashCode.Combine(Voornaam, Geboortejaar);
}
In dit hoofdstuk ontdekte je dat elke klasse in C# uiteindelijk overerft van