WPO 11

Een aantal elementaire concepten zijn intussen aan bod gekomen in de voorgaande WPO's. Om de basis van het programmeren te vervolledigen zullen we een aantal concepten introduceren die het leven van een (beginnende) programmeur soms kunnen vereenvoudigen:

De eerste 2 elementen worden in heel veel programmeertalen toegelaten. Vaak is er hier extra syntax nodig om dit te bewerkstelligen. Het concept achter het doorgeven van argumenten "by reference"/"by out" is dat men op die manier virtueel over meerdere return-statements kan beschikken. Een gewone functie retourneert maximaal 1 enkel waarde via de return-statement. Door argumeten bidirectioneel te maken kan men meer dan 1 return waarde bekomen. Het gebruik van by reference moet echter met de grootste voorzorg gedaan worden om zodoende ongewenste fouten te vermijden. Gebruik deze constructies dus enkel waar ze echt nodig zijn. Tussen "by reference" en "by out" is er een subtiel verschil. In het eerste geval moet het argument al een waarde toegewezen hebben gekregen voordat deze aan de functie meegegeven wordt. In het 2de geval is dit niet nodig (out van output). Het gebruik van "by reference"/"by out" wordt in onderstaande codefragment aangetoond.

//------------------------------------------ private void changevalue(ref int value) { // multiply our value by 2 value = value*2; // no return needed -> pass by reference } //------------------------------------------ private void callerFunction() { // we first need declare our variable and assign a value to it int val = 5; changevalue(ref val); MessageBox.Show(val.ToString()); }
//------------------------------------------ private void setValue(out int value) { value = 10; // no return needed -> pass by reference } //------------------------------------------ private void callerFunction() { // we first need declare our variable; // do not assign a value to it int val; changevalue(out val); MessageBox.Show(val.ToString()); }

In beide gevallen moeten de variabelen gedeclareerd zijn voordat de functie opgeroepen wordt. Het argument wordt steeds voorafgegaan door het woordje out of ref, en dit zelfs voordat de datatype geschreven wordt. Ook in het geval dat de functie opgeroepen wordt, moeten de woorden out of ref gebruikt worden zoals de voorbeelden het aangeven.

Indien men een enkelvoudige variabele "by reference" of "by out" wilt doorgeven, dan is men verplicht om de extra sleutelwoorden te gebruiken. Ook arrays en lijsten kunnen "by reference" doorgegeven worden. Hier moet men echter niets extra voor schrijven. Arrays en lijsten worden impliciet "by reference" doorgegeven.

//------------------------------------------ private void changeArray(int[] arr) { // arrays are implicitely passed by reference for (int i = 0; i < arr.Length; i++) { arr[i] = 2 * arr[i]; } } //------------------------------------------ private void callerFunction() { // we first need declare our array and assign values to it; int[] values = new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; changeArray(values); }
//------------------------------------------ private void changeList(List<int> arr) { for (int i = 0; i < arr.Count; i++) { arr[i] = 2 * arr[i]; } } //------------------------------------------ private void callerFunction() { //first declare and populate our list!! List<int> values = new List<int>(); for (int i = 0; i < 11; i++) values.Add(i); // now we can change the existing values in this list changeList(values); }

Het doorgeven van een array of lijst "by out" kan, maar wordt veel minder gebruikt. Dit wordt hier niet verder toegelicht.

Naast het doorgeven van parameters "by reference"/"by out" kan het soms nuttig zijn om een algoritme op recursieve wijze te implementeren. Recursie wordt bereikt doordat een functie zichzelf oproept. Dit oproepen kan rechtstreeks als onrechtstreeks gebeuren. IN het eerste geval roept de functie zichzelf als functie terug op. In het 2de geval roept de functie een andere functie op, die op zijn beurt de originele functie oproept. Een voorbeeld van recursie wordt hieronder weergegeven.

//------------------------------------------ private int factorial(int value) { if (value>=2) return value*factorial(value-1); // continue recursion here else return 1; // stop recursion here } //------------------------------------------ private void RunFactorial() { MessageBox.Show("Factorial of 10 = " + factorial(10)); }

Als laatste willen we nog de beginselen van het opdelen van code in verschillende codebestanden meegeven. Het zal de aandachtige student al zijn opgevallen dat de code die we schrijven al snel 100 lijnen of meer in beslag neemt. Ondanks de pogingen om de code in aparte functies onder te brengen zal dit alsnog tot een warboel uitgroeien. Om dit tegen te gaan kan men bij elkaar horende blokken code onderbrengen in een apart bestand (klasse). Men kan dan vanuit de hoofdcode de code uit die bestanden aanspreken via functies om zodoende het gewenste resultaat te bekomen. De functies die we hier zullen aanspreken zullen we voortaan methoden noemen. Voor de eenvoud worden hier enkel static-methoden gebruikt. Andere methoden bestaand ook, maar komen in de cursus van objectgeoriënteerd programmeren aan bod. Het gebruik van een dergelijke constructie wordt in onderstaande codefragmenten aangetoond met een aantal functies die het tekenen vergemakkelijken.

// write here a class with static methods which can be access from the main code // this code does reside in another file than the code of the main window public class Drawings { //--------------------------------------------------------------------------------------------------- public static void DrawLine(double x1, double y1, double x2, double y2, Color clr, Canvas cvsDraw) { // put here the code to draw a line on the canvas } //--------------------------------------------------------------------------------------------------- public static void DrawRectangle(double x1, double y1, double w, double h, Color clr, Canvas cvsDraw) { // put here the code to draw a rectangle on the canvas } //--------------------------------------------------------------------------------------------------- public static void DrawEllips(double x1, double y1, double w, double h, Color clr, Canvas cvsDraw) { // put here the code to draw an ellips on the canvas } //--------------------------------------------------------------------------------------------------- }

Het oproepen van deze functies vanuit een knop op de mainwindow geschiedt als volgt:

// this code is in the function of a button Drawings.DrawLine(10,10,100,100,Colors.Red,cvsDraw); Drawings.DrawRectangle(100,100,150,150,Colors.Blue,cvsDraw);

Merk op dat de methode-oproepen nu voorafgegaan worden door de naam van de klasse, nl. Drawings. Om de methoden aanspreekbaar te maken van buiten de klasse Drawings moeten deze public static staan. In andere gevallen zullen de methoden mogelijks niet aanspreekbaar (en dus niet te gebruiken) zijn.