Reduce errors with parameters
Many programming errors are related to improper use of procedure parameters. Declare parameters properly in order to avoid unnecessary errors in your Visual Basic programs.
Each procedure declares zero or more formal parameters. A formal parameter accepts a input value and it can also return a value. This article shows ways to declare formal parameters in order to avoid programming errors and also to keep the program more manageable. The article focuses on Visual Basic: both classic VB and VB.NET.
Declare data types explicitly
Each parameter should explicitly declare a data type (the As Datatype
part). Declaring an explicit data type helps enforce you actually receive reasonable data as input. A numeric data type ensures you will receive a number, a string parameter makes sure you receive a string and so on.
By default, Visual Basic does not require parameters to have an explicit data type. Where the data type is missing, a generic type is used, either Variant (classic VB) or Object (VB.NET). This is no good news. Generic types are too allowing, which means trouble.
Variant and Object are data types to avoid with parameters, unless you are really prepared to accept all kinds of data that can be contained in them: objects, arrays, missing values and so on. Other generic types include Control and Form. Of course you can use generic types, but you need to be prepared to handle the input. Usually this is not what you expect to do with every procedure. That's why it pays off to choose a reasonable data type and declare it explicitly.
Choosing the data type
Use the most specific data type that covers the intended values. By restricting allowed values via the data type declaration you save yourself from unexpected errors. Using specific data type increases the likelihood that errors will be detected early, either during compilation or the first tests.
To prevent negative numbers, use an unsigned data type. In classic VB, Byte is the only unsigned type. In VB.NET you can use Byte, UShort, UInteger or ULong.
To prevent decimal numbers, use an integer data type. In classic VB, Byte, Integer and Long are the alternatives. In VB.NET you have more choices: Byte, SByte, UShort, Short, UInteger, Integer, ULong and Long.
To prevent way too large values, use a narrow integer. As an example, Byte ensures you always receive a value between 0 and 255. Integer (classic VB) or Short (VB.NET) ensures the values will not be outside the range -32768 to 32767.
To accept a flag or a 2-way decision, use a Boolean. This ensures you will receive either True or False no matter what. It is also a way to keep the code more understandable.
Use an Enum data type to provide a list of alternative integer values. As new calls are written to the procedure, IntelliSense provides a list of acceptable values from which to choose from. Even if it's a simple choice of 2 or 3 values that appears just once in your program, declaring an Enum helps make your code more understandable. Keep in mind, though, that the Enum data type doesn't prevent values outside the Enum. Suppose an Enum with values 1, 2 and 3. Nothing prevents a 4 from being passed. It's up to the called procedure to verify the input is good.
To accept really big numbers, Long or ULong may not be enough. In classic VB the Variant data type is a good idea, because it can contain Decimal numbers. In VB.NET use Decimal. Also Double is worth considering.
Too restrictive data types should be avoided. As an example, careless use of Short or Integer may cause problems if unexpectedly high values should come up later.
Optional parameters and ParamArrays
An Optional parameter is a good idea if a reasonable default value is available.
Callers should omit an Optional argument when they intend to pass the default value. Only when a non-default value must be passed should it be given explicitly in the call. If you ever need to change the default, you can just change it in the parameter declaration. The callers don't need to be touched. This way your code stays flexible.
ParamArray is a difficult choice. Don't use it unnecessarily as a fake Optional parameter. Leave it to cases where you absolutely need it.
In vs. out parameters
- An in parameter is one that is used to give an input value to a procedure. This is the regular use for parameters.
- An out parameter is one that returns an output value to the callers. Out parameters are not used for input at all. Any value passed to an out parameter is either ignored or overwritten. In Visual Basic out parameters must be declared as ByRef.
- An in/out parameter is one that receives an input value and returns an output value. The difference to an out parameter is that an in/out parameter is actually read before being overwritten. In Visual Basic in/out parameters must be declared as ByRef.
Out and in/out parameters should generally be avoided. They represent a side effect of the procedure. Such a side effect tends to be hard to understand. One should use the function return value for output. When that is not possible, class-level variables are a good alternative.
One can't always totally avoid out and in/out parameters. They may be required to return multiple output values, especially if you can't use class-level variables to store the output. When you choose to use out or in/out parameters, you should document the use properly.
ByVal vs. ByRef
ByVal and ByRef are related to in and out parameters.
ByVal requires an in parameter. ByVal passes a value. If the procedure changes the value, the changes are not reflected back to the caller. Outputting a value through a ByVal parameter is impossible.
Exception: If the ByVal parameter is an object, one can set the properties of the object. One can't replace the object with another object, though.
ByRef allows any of in, out or in/out. ByRef passes the actual variable, so to speak. If the procedure changes the variable, the changes are reflected back to the caller. Even though ByRef allows output, it does not require it. This means ByRef has an inherent problem. It's not exactly clear whether this is an in or out parameter, or maybe in/out. ByRef parameters should be commented well to make this explicit.
ByRef is best left for out and in/out parameters. Use ByVal for in parameters.
In classic VB it's important to notice that if you omit ByVal and ByRef, the default is ByRef. It's a good idea to add ByVal to make your code safer.
Parameter names
Parameters should be reasonably named. A name serves as a documentation of what the parameter is used for. The use of descriptive names reduces the likelihood of errors. It also decreases the amount of documentation that needs to be written.
Unreasonably short names such as a, b and c should generally be avoided.
If the use of a parameter changes, you should change its name as well.
How many formal parameters?
Zero parameters is no problem. But how many parameters is too much?
A widely suggested maximum number of parameters is 5. Other limits, such as 7, have also been suggested.
Whatever the actual limit, there shouldn't be "too many" parameters. Having to provide a large number of parameters increases the likelihood that you pass them in the wrong order, or that you pass the wrong values.
To reduce the number of required parameters, consider some of these solutions:
- Pass a user-defined data type (structure) or an object containing the required data.
- Pass data via module-level or class variables where appropriate.
- Convert an out parameter to a function return value.
- Convert compulsory parameters to Optional parameters. While this doesn't reduce the total number of formal parameters, it reduces the number of actual arguments that need to be passed.
- When there is heavy parameter passing between several procedures, it may be better to group the procedures and data into a new class. The procedures are tightly coupled by data, and this is exactly what classes are intended for.
Reduce errors with parameters
URN:NBN:fi-fe201012073064