Generics
Generieke methoden
Vaak schrijf je methoden die hetzelfde doen, maar waarvan enkel het type van de parameters en/of het returntype verschilt. Stel dat je een methode hebt die de elementen in een array onder elkaar toont. Je wil dit werkende hebben voor arays van het type int
, string
, enz. Zonder generics moeten we dan per type een methode moeten schrijven:
Dankzij generics kunnen we nu het deel dat generiek moet zijn aanduiden (in dit geval met T
) en onze methode eenmalig definiëren. We gebruiken hierbij de < >
aanduiding die aan de compiler vertelt "dit stuk is een generiek type":
Vanaf nu kun je eender welk soort array aan deze ene methode geven en de array zal naar het scherm afgedrukt worden:
Generic types
We kunnen niet alleen generieke methoden schrijven, maar ook eigen klassen én interfaces definiëren die generiek zijn. In het volgende codevoorbeeld is te zien hoe een eigen generic class in C# gedefinieerd en gebruikt kan worden. Merk het gebruik van de aanduiding T
, deze geeft weer aan dat hier een type (zoals int
, double
, Student
, enz.) zal worden ingevuld tijdens het compileren.
De typeparameter <T>
wordt pas voor de specifieke instantie van de generieke klasse of type ingevuld bij het compileren. Hierdoor kan de compiler per instantie controleren of alle parameters en variabelen die in samenhang met het generieke type gebruikt worden wel kloppen.
De afspraak is om .NET een T
te gebruiken indien het type nog dient bepaald te worden. Dit is niet verplicht maar wordt aanbevolen als je maar 1 generiek type nodig hebt.
We wensen nu een klasse te maken die de locatie in X,Y,Z coördinaten kan bewaren. We willen echter zowel float
, double
als int
gebruiken om deze X,Y,Z coördinaten in bij te houden:
We kunnen deze klasse nu als volgt gebruiken:
Merk op dat het keyword var
hier handig is: het verkort de ellenlange stukken code waarin we toch maar gewoon het datatype herhalen dat ook al rechts van de toekenningsoperator staat.
Een complexere generieke klasse
Voorgaand voorbeeld is natuurlijk maar de tip van de ijsberg. We kunnen bijvoorbeeld volgende klasse maken die we kunnen gebruiken met eender welk type om de meetwaarde van een meting in op te slaan. Merk op hoe we op verschillende plaatsen in de klasse het element T
gebruiken als een datatype:
Een voorbeeldgebruik van dit nieuwe type kan zijn:
Meerdere types in generics
Zoals reeds eerder vermeld is de T
aanduiding enkel maar een afspraak. Je kan echter zoveel T
-parameters meegeven als je wenst. Stel dat je bijvoorbeeld een klasse wenst te maken waarbij 2 verschillende types kunnen gebruikt worden. De klassedefinitie zou er dan als volgt uit zien:
Een object aanmaken zal nu als volgt gaan:
Constraints
We willen soms voorkomen dat bepaalde types wel of niet gebruikt kunnen worden in je zelfgemaakte generieke klasse.
Stel bijvoorbeeld dat je een klasse schrijft waarbij je de CompareTo()
methode wenst te gebruiken. Dit gaat enkel indien het type in kwestie de IComparable
interface implementeert. We kunnen als constraint (beperking) dan opgeven dat de volgende klasse enkel kan gebruikt worden door klassen die ook effectief die interface implementeren (en dus de CompareTo()
-methoden hebben). We doen dit in de klasse-definitie met het nieuwe where
keyword. We zeggen dus letterlijk: "waar T overerft van IComparable":
Volgende gebruik van deze klasse zou dan True
op het scherm tonen:
Mogelijke constraints
Verschillende zaken kunnen als constraint optreden. Naast de verplichting dat een bepaalde interface moet worden geïmplementeerd kunnen ook volgende constraints gelden (bekijk de online documentatie voor meer informatie hierover):
Enkel value types.
Enkel klassen.
Moet default constructor hebben.
Moet overerven van een bepaalde klasse.
Last updated