Soms maken we een parent-klasse waar op zich geen instanties van kunnen gemaakt worden: denk aan de parent-klasse Dier
. Subklassen van Dier kunnen Paard
, Wolf
, etc zijn. Van Paard en Wolf is het logisch dat je instanties kan maken (echte paardjes en wolfjes) maar van 'een dier'? Hoe zou dat er uit zien.
Met behulp van het abstract
kunnen we aangeven dat een klasse abstract is: je kan overerven van deze klasse, maar je kan er geen instanties van aanmaken.
We plaatsen abstract
voor de klasse om dit aan te duiden.
Een voorbeeld:
Volgende lijn zal een error geven: Dier hetDier = new Dier();
We mogen echter wel klassen overerven van deze klasse en instanties van aanmaken:
En dan zal dit wel werken: Wolf wolfje= new Wolf();
En als we polymorfisme gebruiken (zie verder) dan mag dit ook: Dier paardje= new Paard();
Het is logisch dat we mogelijk ook bepaalde zaken in de abstracte klasse als abstract kunnen aanduiden. Beeld je in dat je een Methode "MaakGeluid" hebt in je klasse Dier. Wat voor een geluid maakt 'een dier'? We kunnen dus ook geen implementatie (code) geven in de abstracte parent klasse.
Via abstracte methoden geven we dit aan: we hoeven enkel de methode signature te geven, met ervoor abstract
:
Merk op dat er geen accolades na de signature komen.
Child-klassen zijn verplicht deze abstracte methoden te overriden.
De Paard-klasse wordt dan:
(en idem voor de wolf-klasse uiteraard)
Van zodra een klasse een abstracte methode of property heeft dan ben je, logischerwijs, verplicht om de klasse ook abstract te maken.
Properties kunnen virtual gemaakt, en dus ook abstract
. Volgende voorbeeld toont hoe dit werkt:
Uitgewerkt voorbeeld Abstract en System.Object mbv Zoo-dieren (compilatie uit hoorcollege 18-19)
Voeg ToString toe aan bestaande van volgende projecten. Ik raad aan dat je dit even test in een nieuwe applicatie waarin je de bestaande klasse even toevoegt en niet de hele main overneemt.
Implementeer de ToString() methode in je Pokemon
klasse zodat deze z'n full stats toont wanneer je schrijft:
Implementeer de ToString() methode in zowel de Bookmark
als de HiddenBookmark
klasse. Bij bookmark moet de output bestaan uit de titel van de site, gevolgd door de url tussen haakjes, bv:
Bij HiddenBookmark
wordt er achteraan nog "---HIDDEN---" gezet:
Zorg ervoor dat er géén dubbele code in HiddenBookmark staat (tip: base()
).
Test door volgende code in een eenvoudig programma uit te voeren:
Maak een klasse Book
en gebruik auto-properties voor de velden:
ISBN (int)
Title (string)
Author (string)
Maak voorts een full property voor Price (double)
Maak een child-klasse die van Book overerft genaamd ‘TextBook. Een textbook heeft één extra property:
GradeLevel (int)
Maak een child-klasse die van Book overerft genaamd ‘CoffeeTableBook’. Deze klasse heeft geen extra velden.
Voorts kunnen boeken "opgeteld" worden om als omnibus uitgebracht te worden. De titel wordt dan "Omnibus van [X]". waarbij X de Authors bevat, gescheiden met een komma. De prijs van een Omnibus is steeds de som van beide boeken gedeeld door 2. Schrijf een static
methode TelOp
die twee Book
objecten als parameter aanvaardt en als returntype een nieuw Book
teruggeeft.
In beide child-klassen, override de Price-setter zodat: a) Bij Textbook de prijs enkel tussen 20 en 80 kan liggen b) Bij CoffeeTableBooks de prijs enkel tussen 35 en 100 kan liggen
Zorg ervoor dat boeken de ToString overriden zodat je boekobjecten eenvoudig via Console.WriteLine(myBoek) hun info op het scherm tonen. Ze tonen deze info als volgt: "Title - Auteur (ISBN) Price" (bv The Shining - Stephen King (05848152) 50)
(PRO) Zorg ervoor dat de equals methode werkt op alle boeken. Boeken zijn gelijk indien ze hetzelfde ISBN nummer hebben.
Toon de werking aan van je 3 klassen: Maak boeken aan van de 3 klassen, toon dat de prijs niet altijd zomaar ingesteld kan worden en (PRO) toon aan dat je Equals –methode werkt (ook wanneer je bijvoorbeeld een Book en TextBook wil vergelijken).
Maak enkele klassen die een bank kan gebruiken.
Abstracte klasse Rekening
: deze bevat een methode VoegGeldToe
en HaalGeldAf
. Het saldo van de rekening wordt in een private variabele bijgehouden (en via de voorgaande methoden aangepast) die enkel via een read-only property kan uitgelezen worden. Voorts is er een abstracte methode BerekenRente
de rente als double teruggeeft.
Een klasse BankRekening
die een Rekening is. De rente van een BankRekening is 5% wanneer het saldo hoger is dan 100 euro, zoniet is deze 0%.
Een klasse SpaarRekening
die een Rekening is. De rente van een SpaarRekening bedraagt steeds 2%.
Een klasse ProRekening
die een SpaarRekening is. De ProRekening hanteert de Rente-berekening van een SpaarRekening (base.BerekenRente
) maar zal per 1000 euro saldo nog eens 10 euro verhogen.
Schrijf deze klassen en toon de werking ervan in je main.
Maak een abstracte klasse GeometricFigure
. Iedere figuur heeft een hoogte, breedte en oppervlakte. Maak autoproperties voor van Hoogte
en Breedte
. De oppervlakte is een read-only property want deze wordt berekend gebaseerd op de hoogte en breedte.
Voorzie een abstracte methode BerekenOppervlakte
die een int teruggeeft.
Maak 3 klassen:
Rechthoek: erft over van GeometricFigure. Oppervlakte is gedefinieerd als breedte * hoogte
.
Vierkant: erft over van Rechthoek. Voorzie een constructor die lengte en breedte als parameter aanvaard: deze moeten gelijk zijn, indien niet zet je deze tijdens de constructie gelijk. Voorzie een 2e constructor die één parameter aanvaardt dat dan geldt als zowel de lengte als breedte. Deze klasse gebruikt de methode BerekenOppervlakte van de Rechthoek-klasse.
Driehoek: erft over van GeometricFigure. Oppervlakte is gedefinieerd als breedte*hoogte/2
.
Maak een applicatie waarin je de werking van deze klassen aantoont
Maak een console-applicatie waarin je een zelfverzonnen abstract klasse Dier in een List kunt plaatsen. Ieder dier heeft een gewicht en een methode Zegt
(die abstract is) die het geluid van het dier in kwestie op het scherm zal tonen. Maak enkele childklassen die overerven van Dier en uiteraard de Zegt
methode overriden.
Vervolgens vraag je aan de gebruiker wat voor dieren er in deze lijst moeten toegevoegd worden. Wanneer de gebruiker 'q' kiest stopt het programma met vragen welke dieren moeten toegevoegd worden en komt er een nieuw keuze menu. Het keuze menu heeft volgende opties:
Dier verwijderen , gevolgd door de gebruiker die invoert het hoeveelste dier weg moet uit de List.
Diergewicht gemiddelde: het gemiddelde van alle dieren hun gewicht wordt getoond
Dier praten: alle dieren hun Zegt() methode wordt aangeroepen en via WriteLine getoond
Opnieuw beginnen: de List wordt leeggemaakt en het programma zal terug van voor af aan beginnen.
Probeer zo modulair mogelijk te werken.
Alle klassen C# zijn afstammelingen van de System.Object
klasse. Indien je een klasse schrijft zonder een expliciete parent dan zal deze steeds System.Object als rechtstreekse parent hebben. Ook afgeleide klassen stammen dus af van System.Object. Concreet wil dit zeggen dat alle klassen System.Object-klassen zijn en dus ook de bijhorende functionaliteit ervan hebben.
Because every class descends from
Object
, every object "is an"Object
.
Indien je de System namespace in je project gebruikt door bovenaan using System;
te schrijven dan moet je dus niet altijd System.Object
schrijven maar mag je ook Object
schrijven.
Wanneer je een lege klasse maakt dan zal je zien dat instanties van deze klasse reeds 4 methoden ingebouwd hebben, dit zijn uiteraard de methoden die in de System.Object
klasse staan gedefinieerd:
Stel dat je een klasse Student hebt gemaakt in je project. Je kan dan op een object van deze klasse de GetType() -methode aanroepen om te weten wat het type van dit object is:
Dit zal als uitvoer de namespace gevolgd door het type op het scherm geven. Als je project bijvoorbeeld "StudentManager" heet (en je namespace dus ook) dan zal er op het scherm verschijnen: StudentManager.Student
.
Wil je enkel het type zonder namespace dan is het nuttig te beseffen dat GetType() een object teruggeeft van het type Type
met meerdere eigenschappen, waaronder Name
. Volgende code zal dus enkel Student
op het scherm tonen:
Deze is de nuttigste waar je al direct leuke dingen mee kan doen. Wanneer je schrijft:
Wordt je code eigenlijk herschreven naar:
Op het scherm verschijnt dan StudentManager.Student
. Waarom? Wel, de methode ToString() wordt in System.Object() ongeveer als volgt beschreven:
Merk twee zaken op:
GetType wordt aangeroepen en die output krijg je terug.
De methode is virtual gedefinieerd.
Alle 4 methoden in System.Object zijn virtual
, en je kan deze dus override
'n!
ToString() overriden
Het zou natuurlijk fijner zijn dat de ToString() van onze student nuttigere info teruggeeft, zoals bv de interne Naam (string autoprop) en Leeftijd (int autoprop). We kunnen dat eenvoudig krijgen door gewoon ToString to overriden:
Wanneer je nu Console.WriteLine(stud1);
(gelet dat hij een Naam en Leeftijd heeft) zou schrijven dan wordt je output: Student Tim Dams (Leeftijd:35)
.
Ook deze methode kan je dus overriden om twee objecten met elkaar te testen. Op het einde van deze cursus zal dieper in Equals
ingaan worden om objecten te vergelijken, maar we tonen hier reeds een voorbeeld:
De Equals
methode heeft dus als signatuur: public virtual bool Equals(Object o)
Twee objecten zijn gelijk voor .NET als aan volgende afspraken wordt voldaan:
Het moet false
teruggeven indien het argument o null
is
Het moet true
teruggeven indien je het object met zichzelf vergelijkt (bv stud1.Equals(stud1)
)
Het mag enkel true
teruggeven als volgende statements beide waar zijn:
Indien stud1.Equals(stud2)
true teruggeeft en stud1.Equals(stud3)
ook true is, dan moet stud2.Equals(stud3)
ook true zijn.
Stel dat we vinden dat een student gelijk is aan een andere student indien z'n Naam en Leeftijd dezelfde is, we kunnen dan de Equals-methode overriden als volgt:
De lijn Student temp = (Student)o;
zal het object o
casten naar een Student
. Doe je dit niet dan kan je niet aan de interne Student-variabelen van het object o
.
Indien je Equals override dan moet je eigenlijk ook GetHashCode overriden, daar er wordt verondersteld dat twee gelijke objecten ook dezelfde unieke hashcode teruggeven. Wil je dit dus implementeren dan zal je dus een (bestaand) algoritme moeten schrijven dat een uniek nummer genereert voor ieder niet-gelijke object.
Bekijk volgende StackOverflow post indien je dit wenst toe te passen.
Niet getreurd, je bent niet de enige. Overerving,System.object, Equals,...het is allemaal een hoop nieuwe kennis om te verwerken. Aan het einde van deze cursus gaan we dieper in bovenstaande materie in om een volledige Equals
methode op te bouwen en we bij iedere stap uitgebreide uitleg geven.
Methode
Beschrijving
Equals()
Gebruikt om te ontdekken of twee instanties gelijk zijn.
GetHashCode()
Geeft een unieke code (hash) terug van het object; nuttig om o.a. te sorteren.
GetType()
Geeft het type (of klasse) van het object terug.
ToString()
Geeft een string terug die het object voorstel.