More on Xojo Methods
For beginners, I wrote "Beginner's Guide To Xojo Methods". Now it's time to dive into the topic a bit deeper, but hopefully still keeping it simple.
We have seen in the above article that we can return a value. Perhaps you tried already how to return more values ... and you probably failed. A method can only return one(!) type. But of course, you can return an array or a dictionary. Now arrays are most likely not very beneficial to store results for where you call your method, because it will end up in very "dirty" code and complex (or impossible) to debug. Hence, it is not the best idea to return an array, where you will store a variable x in position 0, and a variable y in position 1, etc. Please, don't do that! A better approach is to work with a dictionary.
With a Xojo dictionary you can return many values, and in the calling method, you can identify the different results by the key of your dictionary. I bet you get the idea, but we want to keep things still simple; if one is not yet familiar with the concept of a dictionary, it will be hard to keep a tutorial for methods accessible. But feel free to play return with dictionaries. They are mighty. They are so powerful that many tutorials explaining "methods" refer to them, but obviously, that's often a bit too much for a beginner.
As I said above, you can only define one return type. That's true, but this doesn't mean that there are no other ways to manipulate or transfer values. In this post, I'll cover:
- Optionals and
When reviewing others' code, I'm often surprised that both concepts are rarely used (if at all).
Let's first build a little example. Please create an app with the following code:
Var value1 As Integer = 10 Var value2 As Integer = 20 Var result As Integer result = myMethod( value1, value2 ) Break
and a method myMethod with the following code:
Var lResult As Integer lResult = value1 + value2 return lResult
The method with its parameters will have to look like this:
This will return the value 30 when executed.
Now let's assume that one day you see the need that under exceptional circumstances, you have to multiply your result by 2. You will now face two issues:
- Either you are adding a new parameter value3, but then you have to ensure that for all cases, but your "special case" value3 will be set to 1 (as then multiplying will have no effect). This will imply that you adapt all the calls of this method, wherever you have used it in your code!
- Or you are using some If-Then statements referring to variables outside of your method's context. Don't do that!
Optionals are optional(!) parameters. This means you can add them without changing formerly written calls (as you don't need to use them in your calls). Consequently we can now change our code like this:
Var lResult As Integer If multiply Then lResult = ( value1 + value2 ) * 2 Else lResult = value1 + value2 End If Return lResult
Var value1 As Integer = 10 Var value2 As Integer = 20 Var result As Integer Var specialResult As Integer result = myMethod( value1, value2 ) specialResult = myMethod( value1, value2, True ) Break
As you can see, you are not forced to call the third parameter, but you can use it in your method wherever you want. To make this work, it is evident that Xojo has to populate the value of the optionals by default values (if you do use them or not). The default values, are the default values per type (false for booleans, 0 for integers, etc.)
Of course, you can "overrule" the default value. Something like the below in the definition of your parameters will work perfectly fine (even though it doesn't make much sense in this case):
value1 as integer, value2 as integer, optional multiply as Boolean = true
I encourage you to play around with this in a small template project to understand the topic better.
One possible use of optionals is for instance when you want to create some PDFs via a method. You might have thought about a standard report only initially, but at a later stage, your customer is asking for the report to show something slightly different if the boss is calling it. Adding such a "flag" via an optional is quite handy, as you only have to adapt your method and don't necessarily need to care about all the former calls of that method in your code.
Again, optionals can be very powerful, but they can quickly become a mess as well. Remember that they are not "empty" but populated at the start of your method by the default values of your variable type, or by what you have defined in the parameter's definition area in the IDE.
For reasons inexplicable to me, many beginners seem to fear the word "byRef". Probably because you don't know that the parameters are passed to a method as "byVal" by default. ByVal is just as terrifying as byRef, isn't it? :-).
Let's start with the default. By default values are "transferred" to your method byVal, means by value. In other words: the value of your variable x in the caller is transferred to the parameter:
Var myVariable1 As integer = 21 Call myMethod( myVariable1 )
Think of it, as if value1 is "translated" by the compiler into:
Call myMethod( 21 )
By default Xojo forgets the "reference" to your variable myVariable1. Xojo only "transfers" the value (means the content, the information) of your variable myVariable1.
This is one reason why your method can't directly change myVariable1 of your calling method. Actually, your method doesn't even know that something like myVariable1 exists in the calling routine.
Again, by default only the content (the values, the information) of your variables are transferred from the main code to the parameters of your method. If you don't believe me: that's the reason why you can define a variable in your caller to be named x and the parameter can be called x as well. Both x are two different variables(!) only the content is transferred to the method! Consequently, there is no way (in the default byVal context) to transfer the value of x in your method back to your caller, other than of course, returning x in your method.
I suspect you already know what will come now. ByRef is indeed changing the above default behavior. With ByRef we are telling the compiler that we don't want to get our variable transferred to a parameter "by value", but via reference. Think of it as defining a pointer. You don't now transfer any longer the value of your variable, but a pointer to the value of your original variable. Let's look at the following simple (but silly) example:
Var value1 As Integer = 10 Var result As Integer = 0 Call myMethod( value1, result ) Break
myResult = myValue + 79
As you can see: we are still using different variable names in our main program and our method and those are still own local (from a method perspective) variables. but myResult (in the method) is pointing (via the byRef statement in parameters) to the transferred variable "result" of the main app and(!) can as such alter it!
If you debug this program you will notice that result in the main app changed from 0 before calling myMethod to 89 after the call finished.
With byRef you can indeed alter indirectly a variable used in the call of a method. This is extremely helpful in many circumstances, but I recommend that you are carefully documenting the use of it. If you are using it very often, and without any comments, your code might easily become cumbersome and a complete mess to debug. The beauty of methods is exactly that you can encapsulate (and re-use) your code, so my own rule of thumb is that I do use byRef only when I have a very good reason doing so. It's for instance not(!) a solution for bypassing the standard way of returning ONE value.
Last but not least: please note that arrays() and objects are ALWAYS transferred to a method in a way, that if your method changes the array or object, the array or object you've used to call the method is altered as well! So it behaves(!) like if an array or an object are transferred byRef. Actually they are not, they are transferred byVal too, but array and objects are by default reference types and not value types, that's why they appear to behave like byRef transfers, though they are actually passed byVal. You can read more about this here:
Don't worry if, as a beginner, you don't understand all those details yet. It should be enough to remember that objects and array "behave" like if they are transferred via byRef to a method.
If your were able to follow me, you will now understand that it doesn't make sense to combine OPTIONAL and byRef: the reason is simple, with byRef the compiler could never be aware what the default value for the optional should be.
By the way, I haven't found a better cover picture. It is supposed to show "twins" because it basically depends on whether we work with the original in our method or with the copy ...
Sign in or become a jeannot-muller.com member to join the conversation.
Just enter your email below to get a log in link.