Tot polymorfische technieken horen ook:
Het werken met polymorfisme kan vermijden dat je in een programma gebruik moet maken van switch-logica: situaties onderscheiden met een switch-statement. Switch-structuren kunnen voor problemen zorgen doordat je ervoor moet zorgen dat het onderzocht element steeds van hetzelfde type is, en moeten bij elke nieuwe situatie steeds worden aangepast, wat een tijdrovende bezigheid kan zijn.
using System;
namespace Lonen
{
/// <summary>
/// Abstracte klasse Werknemer.
/// </summary>
public abstract class Werknemer
{
private string naam;
private string voornaam;
public Werknemer(string naam, string voornaam)
{
Naam = naam;
Voornaam = voornaam;
}
public string Voornaam
{
get
{
return voornaam;
}
set
{
voornaam = value;
}
}
public string Naam
{
get
{
return naam;
}
set
{
naam = value;
}
}
public override string ToString()
{
return Voornaam +" " +Naam;
}
public abstract decimal Verdiensten();
}
}
using System;
namespace Lonen
{
/// <summary>
/// Klasse Baas.
/// </summary>
public class Baas: Werknemer
{
private decimal loon;
public Baas(string naam, string voornaam, decimal loon )
: base(naam, voornaam)
{
WekelijksLoon = loon;
}
public decimal WekelijksLoon
{
get
{
return loon;
}
set
{
if(value > 0) loon = value;
}
}
public override decimal Verdiensten()
{
return WekelijksLoon;
}
public override string ToString()
{
return "Baas: " +base.ToString();
}
}
}
using System;
namespace Lonen
{
/// <summary>
/// Klasse CommissieWerker.
/// </summary>
public class CommissieWerker: Werknemer
{
private decimal loon;
private decimal commissie;
private int aantal;
public CommissieWerker(string naam, string voornaam, decimal loon,
decimal commissie, int aantal)
: base(naam, voornaam)
{
WekelijksLoon = loon;
Commissie = commissie;
Aantal = aantal;
}
public decimal WekelijksLoon
{
get
{
return loon;
}
set
{
if(value > 0) loon = value;
}
}
public decimal Commissie
{
get
{
return commissie;
}
set
{
if(value > 0) commissie = value;
}
}
public int Aantal
{
get
{
return aantal;
}
set
{
if(value > 0) aantal = value;
}
}
public override decimal Verdiensten()
{
return WekelijksLoon + Commissie * Aantal;
}
public override string ToString()
{
return "CommissieWerker: " +base.ToString();
}
}
}
using System;
namespace Lonen
{
/// <summary>
/// Summary description for StukWerker.
/// </summary>
public class StukWerker: Werknemer
{
private decimal loon;
private int aantal;
public StukWerker(string naam, string voornaam,
decimal loonPerStuk, int aantal)
: base(naam, voornaam)
{
LoonPerStuk = loonPerStuk;
Aantal = aantal;
}
public decimal LoonPerStuk
{
get
{
return loon;
}
set
{
if (value > 0) loon = value;
}
}
public int Aantal
{
get
{
return aantal;
}
set
{
if (value > 0) aantal = value;
}
}
public override decimal Verdiensten()
{
return Aantal * LoonPerStuk;
}
public override string ToString()
{
return "Stukwerker: " +base.ToString();
}
}
}
using System;
namespace Lonen
{
/// <summary>
/// klasse UurWerker.
/// </summary>
public class UurWerker: Werknemer
{
private decimal loon;
private double uren; //uren gewerkt tijdens de week
public UurWerker(string naam, string voornaam, decimal loon, int aantalUren)
: base(naam, voornaam)
{
Loon = loon;
Uren = aantalUren;
}
public decimal Loon
{
get
{
return loon;
}
set
{
if(value > 0) loon = value;
}
}
public double Uren
{
get
{
return uren;
}
set
{
if(value > 0) uren = value;
}
}
public override decimal Verdiensten()
{
decimal loon = Loon * Convert.ToDecimal(Uren);
if(Uren > 40)
{
loon += Convert.ToDecimal(Uren - 40) * Loon * 1.5M;
}
return loon;
}
public override string ToString()
{
return "Uurwerker: " +base.ToString();
}
}
}
private void Form1_Load(object sender, System.EventArgs e)
{
string uit = "";
Werknemer werknemer;
Baas baas = new Baas("Speckneck","Bill",1000);
CommissieWerker commissiewerker = new CommissieWerker("Zetter","Af",200M,10M,5);
StukWerker stukwerker = new StukWerker("Adoor","Stuk",100M,5);
UurWerker uurwerker = new UurWerker("Overuurs","Fien",10,50);
uit += baas.ToString() +" verdient " + baas.Verdiensten().ToString("c") +"\n";
werknemer = baas;
uit += maakString(werknemer);
uit += commissiewerker.ToString() +" verdient " + commissiewerker.Verdiensten().ToString("c") +"\n";
werknemer = commissiewerker;
uit += maakString(werknemer);
uit += stukwerker.ToString() +" verdient " + stukwerker.Verdiensten().ToString("c") +"\n";
werknemer = stukwerker;
uit += maakString(werknemer);
uit += uurwerker.ToString() +" verdient " + uurwerker.Verdiensten().ToString("c") +"\n";
werknemer = uurwerker;
uit += maakString(werknemer);
L.Text = uit;
}
public static string maakString(Werknemer werker)
{
return werker.ToString() +" verdient " +werker.Verdiensten().ToString("c") +"\n\n";
}
Elk object wordt telkens gestopt in een variabele van het type Werknemer. Na deze toekenning wordt de methode maakString van de GUI-klasse uitgevoerd. Deze methode ontvangt een Werknemer en retourneert een string na toepassen van de methoden ToString en Verdiensten van de klasse Werknemer.
Je merkt dat na de toekenning toch voor elk soort werknemer de juiste loonsberekening wordt uitgevoerd!
Dit is de kracht van polymorfisme: de methode maakString hoef je slechts 1 keer aan te maken. Deze methode ontvangt een instantie van de basisklasse Werknemer.
De superklasse Werknemer kan de juiste methoden van de afgeleide klassen aanroepen!
Dit fenomeen kan je handig aanwenden voor het maken van herbruikbare methoden.
Opmerking: dit fenomeen treedt ook op wanneer de methode in de superklasse niet abstract is. Doe bijvoorbeeld volgende aanpassing in de klasse Werknemer:
public virtual decimal Verdiensten() { return 300M; }
public delegate double EenheidsOmzetting(double gegeven);
In deze toepassing maken we een delegate EenheidsConversie die een double ontvangt en ook een double retourneert. Het is de bedoeling dat de gebruiker een waarde uit een bepaalde eenheid kan omzetten in een andere eenheid.
We maken een functie MijlNaarKM met eenzelfde signatuur als de delegate.
We maken een instantie ec van de delegate EenheidsConversie en geven mee dat we de methode MijlNaarKm willen gebruiken bij het uitvoeren van de delegate.
We kunnen nu de delegate gebruiken precies zoals een gewone methode en hoeven ons niet af te vragen welke de achterliggende methode (MijlNaarKm) is die zal worden uitgevoerd.
namespace Delegates
{
// 1. definieer Delegate
public delegate double EenheidsConversie(double gegeven);
public class Form1 : System.Windows.Forms.Form
{
...
// 2. definiëer handler-methode
public static double MijlNaarKm(double mijl)
{
return mijl * 1.609344;
}
private void btnMijlNaarKM_Click(object sender, System.EventArgs e)
{
// 3. Maak een instantie van de delegate
EenheidsConversie ec = new EenheidsConversie(MijlNaarKm);
// 4. Gebruik de delegate net zoals een methode
double mijl = ec(double.Parse(txtGegeven.Text));
lblResultaat.Text = mijl.ToString();
}
}
Deze tweede handler-functie heeft opnieuw eenzelfde signatuur als de delegate.
Analoog aan het eerste voorbeeld kunnen we deze handler-functie bereiken door een instantie van de delegate te declareren met als argument de naam van deze handler-functie. Wanneer we nu de delegate uitvoeren wordt de conversie van celsius in Kelvin gedaan.
namespace Delegates
{
// 1. definieer Delegate
public delegate double EenheidsConversie(double gegeven);
public class Form1 : System.Windows.Forms.Form
{
...
// 2. definiëer handler-methode
public static double MijlNaarKm(double mijl)
{
return mijl * 1.609344;
}
// 2. een andere handler methode
public static double CelsiusNaarKelvin(double celsius)
{
return celsius + 274.15;
}
private void btnMijlNaarKM_Click(object sender, System.EventArgs e)
{
// 3. Maak een instantie van de delegate
EenheidsConversie ec = new EenheidsConversie(MijlNaarKm);
// 4. Gebruik de delegate net zoals een methode
double mijl = ec(double.Parse(txtGegeven.Text));
lblResultaat.Text = mijl.ToString();
}
private void btnCelsiusNaarKelvin_Click(object sender, System.EventArgs e)
{
EenheidsConversie ec = new EenheidsConversie(CelsiusNaarKelvin);
double kelvin = ec(double.Parse(txtGegeven.Text));
lblResultaat.Text = kelvin.ToString();
}
}
We maken nu een methode die intern bijhoudt welke conversie er dient te gebeuren. De methode GetConversieMethode ontvangt een int en retourneert een instantie van de delegate EenheidsConversie. Binnen deze methode wordt de instantie van EenheidsConversie aangemaakt naargelang de meegegeven integer.
We noemen de functie GetConversieMethode een factory-methode daar het niet de bedoeling is dat ze rechtstreeks wordt aangeroepen, ze wordt steeds opgeroepen vanuit een andere methode.
Hier zie je ook een methode maakResultaat: deze methode ontvangt een double die het om te zetten getal bevat en een int die het type van omzetting bepaalt. De functie retourneert een double: het omgezette getal.
Op deze manier wordt het mogelijk een omzettingsfunctie te gebruiken door enkel het meegeven van een integer. Je kan hier natuurlijk ook een enumeratie voor voorzien, dan wordt het nog transparanter.
namespace Delegates
{
// 1. definieer Delegate
public delegate double EenheidsConversie(double gegeven);
public class Form1 : System.Windows.Forms.Form
{
...
// 2. definiëer handler-methode
public static double MijlNaarKm(double mijl)
{
return mijl * 1.609344;
}
// 2. een andere handler methode
public static double CelsiusNaarKelvin(double celsius)
{
return celsius + 274.15;
}
/// <summary>
/// methode voor het bepalen van de soort conversie
/// </summary>
/// <param name="conversionType">integer die het type aangeeft</param>
/// <returns>Een pointer naar de respectievelijke delegatefunctie</returns>
public EenheidsConversie GetConversieMethode(int conversieType)
{
EenheidsConversie conversie;
switch (conversieType)
{
case 1:
conversie = new EenheidsConversie(MijlNaarKm);
break;
case 2:
conversie = new EenheidsConversie(CelsiusNaarKelvin);
break;
default:
throw new ArgumentException("Conversietype niet herkend: " + conversieType);
}
return conversie;
}
/// <summary>
/// Doet de omzetting volgens het opgegeven conversietype
/// Eerst maken we een EenheidsConverie object met de methode GetconversieMethode
/// het type krijgen we als integer binnen
/// Daarna voeren we de respectievelijke delegatefunctie uit met de meegegeven double-waarde
/// </summary>
/// <param name="waarde">waarde die we wensen om te zetten</param>
/// <param name="type">integer die het type omzetting voorstelt</param>
/// <returns></returns>
private double MaakResultaat(double waarde, int type)
{
EenheidsConversie ec = GetConversieMethode(type);
return ec(waarde);
}
private void btnMijlNaarKM_Click(object sender, System.EventArgs e)
{
lblResultaat.Text = MaakResultaat(double.Parse(txtGegeven.Text),1).ToString();
}
private void btnCelsiusNaarKelvin_Click(object sender, System.EventArgs e)
{
lblResultaat.Text = MaakResultaat(double.Parse(txtGegeven.Text),2).ToString();
}
}
}
![]() |
Je kan meerdere handler-functies toekennen aan een instantie van een delegate.
Wanneer je de delegate uitvoert zullen alle handler-functies uitgevoerd worden. Het toekennen van een handlermethode gebeurde in de bovenstaande voorbeelden met de operator = .
Wanneer je de instantie nu uitvoert worden alle delegate-handlermethodes die werden toegekend uitgevoerd. |
namespace Delegates
{
// 1. definieer Delegate
public delegate double EenheidsConversie(double gegeven);
public class Form1 : System.Windows.Forms.Form
{
...
// 2. definiëer handler-methode
public double MijlNaarKm(double mijl)
{
double res = mijl * 1.609344;
lblRapport.Text += "Mijl naar KM: " +res.ToString() +"\n";
return res;
}
// 2. een andere handler methode
public double CelsiusNaarKelvin(double celsius)
{
double res = celsius + 274.15;
lblRapport.Text += "Celsius naar Kelvin: " +res.ToString() +"\n";
return res;
}
/// <summary>
/// methode voor het bepalen van de soort conversie
/// </summary>
/// <param name="conversionType">integer die het type aangeeft</param>
/// <returns>Een pointer naar de respectievelijke delegatefunctie</returns>
public EenheidsConversie GetConversieMethode(int conversieType)
{
EenheidsConversie conversie;
switch (conversieType)
{
case 1:
conversie = new EenheidsConversie(MijlNaarKm);
break;
case 2:
conversie = new EenheidsConversie(CelsiusNaarKelvin);
break;
default:
throw new ArgumentException("Conversietype niet herkend: " + conversieType);
}
return conversie;
}
/// <summary>
/// Doet de omzetting volgens het opgegeven conversietype
/// Eerst maken we een EenheidsConverie object met de methode GetconversieMethode
/// het type krijgen we als integer binnen
/// Daarna voeren we de respectievelijke delegatefunctie uit met de meegegeven double-waarde
/// </summary>
/// <param name="waarde">waarde die we wensen om te zetten</param>
/// <param name="type">integer die het type omzetting voorstelt</param>
/// <returns></returns>
private double MaakResultaat(double waarde, int type)
{
EenheidsConversie ec = GetConversieMethode(type);
return ec(waarde);
}
private void btnMijlNaarKM_Click(object sender, System.EventArgs e)
{
lblResultaat.Text = MaakResultaat(double.Parse(txtGegeven.Text),1).ToString();
}
private void btnCelsiusNaarKelvin_Click(object sender, System.EventArgs e)
{
lblResultaat.Text = MaakResultaat(double.Parse(txtGegeven.Text),2).ToString();
}
private void btnRapport_Click(object sender, System.EventArgs e)
{
int aantalOmzettingen = 2; // aantal uit te voeren omzettingen
EenheidsConversie conversies = null;
for(int type = 1; type <= aantalOmzettingen; type++)
{
// delegate methoden toevoegen aan EenheidsConversie-object
conversies += GetConversieMethode(type);
}
// Doordat we met voorgaande lus meerdere delegate-functies hebben toegevoegd worden deze nu allen uitgevoerd !
conversies(double.Parse(txtGegeven.Text));
}
}
}
Wanneer een delegate-instantie geen delegatemethoden meer bezit wordt het object ingesteld op null. Je kan op deze manier controleren of er een methode kan worden uitgevoerd.
if (conversies != null)
{
conversies(waarde);
}
else
{
Console.WriteLine("Geen conversies geselecteerd.");
}
De onderliggende implementatie van een event is in .net eigenlijk een delegate.
Events hebben enkel nog een aantal andere beveiligingen: bij een delegate kan je de handlermethodes onmiddellijk loskoppelen met een toekenning (=), bij events niet: enkel de operatoren += en -= zijn toegestaan.
Event-handlers kunnen niet uitgevoerd worden van buiten de omvattende klasse.
| Meer tutorials: |
| leer ook: | html | | xhtml | | css | | asp | | asp.net | | c# | | ado.net | | linq | | ajax | | java | | javascript |