4Overerving en abstractie
Een subklasse (derived) erft velden, properties en methodes van de superklasse (base). C# staat één basisklasse toe, maar meerdere interfaces. Gebruik overerving alleen bij een echte is-een-relatie; bij heeft-een gebruik je compositie.
public abstract class Voertuig { public string Id { get; } public string Merk { get; } public bool IsBeschikbaar { get; set; } = true; protected Voertuig(string id, string merk) // protected! { if (string.IsNullOrWhiteSpace(id)) throw new ArgumentException("Id mag niet leeg zijn.", nameof(id)); Id = id; Merk = merk; } public abstract decimal BerekenPrijs(int minuten); // geen body public virtual string Omschrijving() => "Voertuig"; // mag overriden public override string ToString() => $"{GetType().Name} {Id} ({Merk}) - " + (IsBeschikbaar ? "beschikbaar" : "verhuurd"); }
Uitleg
De abstracte basis van alle voertuigen. Id en Merk zijn alleen-lezen en worden in de constructor gezet, met een controle op een leeg id. BerekenPrijs is abstract (elk voertuig rekent anders), Omschrijving is virtual (standaard aanwezig, mag aangepast). GetType().Name geeft de naam van de echte subklasse.
abstract- Klasse kan niet met
newgemaakt worden; een abstracte methode heeft geen body en moet in de subklasse worden ingevuld. virtual- Methode heeft een standaardversie die een subklasse mag vervangen met
override. protected- Zichtbaar in deze klasse en in subklassen, niet daarbuiten.
nameof- Geeft de naam van een variabele of parameter als tekst, en blijft kloppen als je hem hernoemt.
string.IsNullOrWhiteSpacetrueals de tekst null, leeg of alleen spaties is.throw- Gooit een exception; de methode stopt direct en de fout gaat omhoog naar de aanroeper.
ArgumentException- Voor een ongeldig argument, zoals een lege tekst of verkeerde combinatie.
GetType().Name- Naam van het echte type van het object, ook als de variabele een ouder-type heeft.
override- Vervangt een virtual of abstracte methode van de ouder; tijdens runtime wordt de versie van het echte object gekozen.
{ get; }- Alleen-lezen property; de waarde wordt in de constructor gezet.
=>- Korte schrijfwijze voor een methode of property die één expressie teruggeeft; in LINQ ook de pijl van een lambda.
$"...{x}"- String-interpolatie: zet de waarde van
xdirect in de tekst.
public class Fiets : Voertuig { private const decimal PrijsPerMinuut = 0.10m; public int Versnellingen { get; } public Fiets(string id, string merk, int versnellingen) : base(id, merk) // eerst de ouder! { Versnellingen = versnellingen; } public override decimal BerekenPrijs(int minuten) => minuten * PrijsPerMinuut; public override string Omschrijving() => "Fiets"; public override string ToString() => $"{base.ToString()}, {Versnellingen} versnellingen"; }
Uitleg
Een concrete subklasse. : base(id, merk) geeft de gegevens door aan de constructor van Voertuig, die eerst draait. Met override vul je de abstracte methode in en pas je ToString() aan, waarbij base.ToString() de tekst van de ouder hergebruikt.
base(...)- Roept de constructor van de ouderklasse aan, vóór de eigen constructor draait.
base.Methode()- Roept de versie van de ouderklasse aan, bijvoorbeeld om die tekst aan te vullen.
override- Vervangt een virtual of abstracte methode van de ouder; tijdens runtime wordt de versie van het echte object gekozen.
private- Alleen zichtbaar binnen deze klasse.
const- Vaste waarde die bij het compileren al vastligt en nooit verandert.
{ get; }- Alleen-lezen property; de waarde wordt in de constructor gezet.
=>- Korte schrijfwijze voor een methode of property die één expressie teruggeeft; in LINQ ook de pijl van een lambda.
$"...{x}"- String-interpolatie: zet de waarde van
xdirect in de tekst. ToString()- Tekstweergave van een object; wordt automatisch gebruikt bij
Console.WriteLine(obj).
- Volgorde: eerst constructor van de ouder, dan die van het kind.
- Zonder
: base(...)roept C# de parameterloze ouder-constructor aan. Bestaat die niet → fout CS7036. base.ToString()hergebruikt de ouder-versie en vult aan.sealed class X: niemand mag van X erven.sealed override: verder overriden verboden.
#virtual, abstract, override, new
| Kenmerk | virtual | abstract |
|---|---|---|
| Implementatie in basisklasse | Ja (standaardgedrag) | Nee (geen body, eindigt op ;) |
| Moet overschreven worden | Nee, mag | Ja, door elke concrete subklasse |
Klasse moet abstract zijn | Nee | Ja |
| Gebruik als | je een standaard wilt die aanpasbaar is | elke subklasse het verplicht anders doet |
override = echte vervanging; wordt tijdens runtime gekozen op basis van het echte object (dynamic dispatch).
new (hiding) = verbergt de ouder-methode; wordt compile-time gekozen op basis van het type van de variabele. Bijna nooit wat je wilt.
// Stel: Dier heeft public virtual void Beweeg() Dier d = new Hond("Rex"); d.Beweeg(); // override in Hond → "Hond rent." // new in Hond → "Dier beweegt." (!)
Uitleg
Het verschil tussen override en new: bij override kijkt C# naar het echte object (een Hond), bij new naar het type van de variabele (Dier). Daarom geeft new hier onverwacht de Dier-versie.
#Abstracte klasse
- Kan niet met
newgemaakt worden (fout CS0144). - Mag velden, constructors, gewone én abstracte methodes hebben.
- Bedoeld als gemeenschappelijke basis: "is een".
- Gedeelde code in twee subklassen? Maak een abstracte tussenklasse (bv.
ElektrischVoertuig : Voertuig, IOplaadbaar).