The Prism Primer - Part 5
From The Oxygene Language Wiki
This is a General topic
Feel free to add your notes to this topic below.
The Prism Primer: Part 1 – Part 2 – Part 3 – Part 4 – Part 5 – Part 6 – Part 7 – Part 8 – Part 9
Methods
Methods are basically a way to write sourcecode you want to use at different places in your program only once. This "common code" is seperated into a method which gets a name, so you can execute the source code (the "method body") by simply using the name of the method. You know that concept from the real world: "Make tea" is a method name and heatening water, filling filter with tea, lighting up the candle, etc. is the method body.
To show how methods work in your program, the calculator sample will be extended once again. The calculator gets a function to swap the values of the two input fields. Please add a new button named "btSwap" to your calculator and double-click on it to get the code editor.
To swap two variables, we have to introduce a temporary "helper" variable. The value from the second variable will be written to this helper variable. Then, after we have "backup" of this value, we can put the first variable's value into the second variable. And then "restore" the original value of the second variable from the helper into the first variable. Swapping complete :-)
We could implement this algorithm directly in the click event's method (yep, that's a method, too!). But topic of this "lesson" is to write our own method. So, please add a new method above the click event's method:
method MainForm.Swap(var left, right : String); begin end;
As you can see from the word "MainForm", this method also belongs to the MainForm class. This is the implementation part of the method, where we actually write what this method will do. But that's not enough. We have to declare the method in the interface section, too, because there is defined what methods, properties, etc. a class has.
Luckily we can just press Ctrl+Shift+C and Delphi Prism extends the class declaration automatically for us. It jumps to the interface section, so we can see what was done. It should look like this:
MainForm = partial class(System.Windows.Forms.Form) private method Swap(var left, right : String); //this was created by Prism method btAdd_Click(sender: System.Object; e: System.EventArgs); method btDivide_Click(sender: System.Object; e: System.EventArgs); method btFactorial_Click(sender: System.Object; e: System.EventArgs); method btSwap_Click(sender: System.Object; e: System.EventArgs); (* some stuff we talk about later *) end;
As you can see, the methods for the click events are declared there, too. Press Ctrl+Shift+DownArrow to return to the implementation for "our" method.
The implementation of the swapping algorithm is simple:
var temp := right; right := left; left := temp;
The method has two parameters: left and right, both of type string. Inside the method they are used like variables. That's not always possible in this exact way, see "Call by Value" later in this text. A third variable temp (the "helper variable") is created and the value of right is assigned to it. right then gets the value of left and left gets the value of temp.
Now we can call this method from the click method of btSwap:
var first := tbFirstNumber.Text; var second := tbSecondNumber.Text; Swap(var first, var second); tbFirstNumber.Text := first; tbSecondNumber.Text := second;
We know the first two lines very well, but this time we are just using the texts (strings) and do not convert them into numbers. After the texts are read into the variables, we cal the Swap methods with these variables. Note that the variables don't have to have the same names as the parameters in the method. The parameters in the method are identifies only by their order!
The last two lines write the now swapped values from the variables back into the textboxes. Now the content of the textboxes is swapped.
Call by Reference
The sourcecode looks a little bit long winded and you may ask yourself, why we need the variabled first and second instead of directly passing the properties of the textboxes to the swap method. The reason for that is the var keyword, which is used in the declaration of the method and the call to the method.
This keyword has the effect, that everything you do with the parameters inside the method, changes the variables that were passed as parmaeters, too! So a change of left in the Swap method does change first in the click method, too. Actually, that's why the Swap method works ;-)
Passing parameters that way is called "Call by Reference". The method only gets a reference to a variable as parameter. In other words: You tell the method, which other external variable should be changed. If we would assign the value "foo" to left in the Swap method, first would have the exact same value. In fact, first and left are the same variable, just with differnt names.
That's the reason why you can't use the Text properties as parameters. They're no variables but properties, which is a big difference and makes it impossible to use them in "call by reference". If you try, you'll get a compiler error like this one:
Only variables can be passed as "var" or "out" for parameter
Very important is to remember the rule, that you not only have to declare the parameters of a method with var, but also have to pass variables using var to the method. So, this won't work:
Swap(first, var second);
because you're missing the var in the method call. You'll get this error message:
Parameter "1" modifier mismatch: was [in] should be var
Call by Value
If you only want to work with parameters inside the method and don't want them to be changed outside, you can pass them using "call by value". You simply declare (and pass) the parameters without var to do so. Now only the values of the parameters are passed to the method, inside the method you're working with copies of them and so do not change their outside value.
Important: Classes are a great exception to this rule! We'll learn about them later, but keep in mind, that there's this exception.
Perhaps you want to try this: Change the Swap method to "call by value" and then run the program. You'll see, that the content of the textboxes won't be swapped. The reason is, that the parameters left and right inside the method are changed, but because they're only copies, the variables first and second outside the method stay the same.
Returning Values
A method can not only execute source code, but can also return a value as result of its execution. For example you can write a method Add, which gets two parameters and then returns the sum of these parameters. To demonstrate this, we'll look at a new way of calculating the factorial of a number, using recursion.
You can speak of a recursion, when a method calls itself. The factorial of a number is a classic example for this approach: The factorial of 5 is 5! = 5*4*3*2*1. You can write this as 5! = 5 * 4!, so you can calculate the factorial of 5 by calculating the factorial of 4 and multiplying it with 5. The sense of recursion is, that you reduce your problem step by step until you get to a point where the solution is obvious: 2! = 2.
Please find the click method for the factorial-button and a new method MainForm.Factorial(n : Integer) : Integer above it. Don't forget to complete the interface section by hitting Ctrl+Shift+C.
method MainForm.Factorial(n : Integer) : Integer; begin if n <= 0 then exit 0; if n <= 2 then result := n else result := n * Factorial(n-1); end;
This method gets a parameter n using "call by value", because we don't need to change this parameter inside our method. The difference to a method not returning a value is the last part of the declaration " : Integer ". This is the return type of the method. As you know, every variable in Delphi Prism has to have a type and that's true for the return value of a method, too. By defining its return type, you turn a method without return value into a method that has one.
The first if-statement checks if we can calculate a sensefull value for n!. If not we return zero, which is mathematically wrong but enough for us at the moment. If we can't calculate n! we directly leave the method by using the exit command. It cancels the execution of the method at this point. The value behind exit is the return value of the method that is used for this "emergency brake". You can use exit in a method without return value by omiting the value behind it.
The next if-statement is important for the recursion. At some point the method must return a value without the recursion, otherwise it would run infinitely. That's a common mistake when using recursion, be carefull avoiding it. We avoid it by checking if the value of n! is obvious, which is the case for n <= 2, because then it's just n.
result is the return value of the method. You can use it like a variable which has the method's return type as variable type. You needn't and mustn't declare it. The last value assigned to result is the return value of the method.
If the value of n! is not obvious, we call the Factorial method again, but with the parameter reduced by one. That makes sure that at some point n will be small enough to calculate n! without recursion.
By the way: You can use a nice "built in" method of Delphi Prism to replace the second if-statement:
result := iif(n <= 2, n, n * Factorial(n-1));
iif gets a boolean value as first parameter. If this value is true, the second parameter is returned as result, otherwise the third one.
Calling the method and using its return value is very easy:
var firstNumber := Integer.Parse(tbFirstNumber.Text); var resultNumber := Factorial(firstNumber); tbResult.Text := resultNumber.ToString;
Notice, that the parameter is passed without var, because it's declared as "call by value" (without var, too).
Out Parameters
You can return values in another way: Using "out"-parameters. They're very similar to "call by reference", but are declared with out instead of var. Out parameters are only there to write values to, but not to read from them. Infact you're very stronlgy encouraged to have written a value into an out parameter before your method finishes. Delphi Prism will show a warning if you haven't done that.
There's a good example for out parameters in the .NET framework: the TryParse methods. We already know the Parse methods, which will raise an exception if the string value can't be parsed into a number. The TryParse is declared as:
method Integer.TryParse(s : String; out result : Integer) : Boolean
(As part of the framework this method was written in C#, where result isn't a reserved word. You wouldn't use that name for a parameter in Delphi Prism.)
You can use it like this:
var firstNumber : Integer; if Integer.TryParse(tbFirstNumber.Text, out firstNumber) then (* ... use the number ... *) else MessageBox.Show('FirstNumber cannot be parsed');
First we have to declare the firstNumber variable. That's actually one of the rare cases, where you have to declare a variable in Delphi Prism and cannot use type inference. The TryParse method is then called inside the if-statement. Its boolean return value indicates wether or not the text was successfully converted into a number. If it was successfull, the number can be found in the out-parameter firstNumber and can be worked with. Otherwise an error message is displayed.
Note that you have to pass an out-parameter using the out just like you had to use the var parameter for reference parameters.
Of course methods without a "normal" return value can have out-parameters, too.
Why Methods?
Methods are not ends in themselves. There're important and very clear reasons why one should use methods:
- You can save yourself much work! If you want to calculate the factorial of a number at more than one place in your program, it makes completely sense to write the algorithm once as method and only call it from where you need it. And that's not only true for the use in one program, but you can use a method in other programs, too!
- The sourcecode gets clearer! If the nesting depth of a sourcecode (for example because of many if-commands and loops inside each other) gets too big, it's not very readable any more. That leads to the strong possibility of more mistakes.
- Methods have names! If you read a sourceode calling a method "Factorial", you directly know what it does.
- The sourceocde gets more maintainable. If you find a bug in the calculation of the factorial, you have to fix this bug only once in one method. If you hadn't used a method, you had to search for all places where you calculated a factorial.
See Also
The Prism Primer: Part 1 – Part 2 – Part 3 – Part 4 – Part 5 – Part 6 – Part 7 – Part 8 – Part 9
Glossaries — Keywords — Types — FAQ — How To