Sub and Function Procedures
Subs and Functions are blocks of code in your program that are called upon from other parts of your program. Subs and Functions are the building blocks of a program and provide a way to organize and modularize your code.
The difference between a Sub and a Function is that a Sub does not produce a return value (i.e., one that can be assigned directly to a variable, whereas a Function does produce a return value). Both Subs and Functions can be called with or without parameters.
The statement below calls a Sub named "AddEm" (you "call" or invoke a Sub by specifying its name followed by parentheses):
AddEm()
The Sub "AddEm" would be a section of code in this format:
Private Sub AddEm()
[statements]
End Sub
When the statement "AddEm()" is executed, VB would execute the statements in the Sub named "AddEm" and then return to the statement the followed "AddEm()".
Example (Calling a Sub Without Parameters)
When the sample program below is run, the user is prompted to enter two numbers. The Addem Sub is called, which performs the addition. Program flow then returns to the Console.WriteLine statement, which displays the sum of the two numbers.
Note that the variables involved are declared at the module-level (i.e., general declarations section, or the section of the module prior to any Subs or Functions) because the variables are common to both the main method and the AddEm Sub procedure.
Code:
Module Module1
'Module-level variables are needed for this example
Private mintNum1 As Integer
Private mintNum2 As Integer
Private mintSum As Integer
Sub Main()
Console.Write("Enter first number: ")
mintNum1 = Val(Console.ReadLine())
Console.Write("Enter second number: ")
mintNum2 = Val(Console.ReadLine())
AddEm()
Console.WriteLine("The sum is: {0}", mintSum)
Console.WriteLine("")
Console.WriteLine("(Press Enter to close this window.)")
Console.ReadLine()
End Sub
Private Sub AddEm()
mintSum = mintNum1 + mintNum2
End Sub
End Module
Screen-shot:
Download the project code for the example above here.
More often than not, a Sub procedure will be expecting one or more parameters. This enhances the reusability and flexibility of the Sub procedure. The syntax for a Sub procedure is:
[Private | Public] Sub SubName[(parameter list)]
[statements]
End Sub
where "parameter list" is a comma-separated list of the parameter variables with their data types (i.e., var1 As datatype, var2 As datatype, etc.).
For example, if we were to modify the "AddEm" Sub to accept three Integer variables, the header for the "Addem" Sub would look like this:
Private Sub AddEm(pintNum1 As Integer, pintNum2 As Integer, pintSum As Integer)
The call to AddEm must now specify, or pass, three integer variables to the Sub
AddEm(intNum1, intNum2, intSum)
The important thing is that the argument list of the calling statement must match the parameter list of the Sub procedure one-for-one in terms of both the number of variables passed and the data types of the variables passed. The names of the corresponding argument/parameter variables can be (and often are) different. With the naming conventions used in these tutorials, the variable names of the parameters in a Sub or Function header start with the letter "p" for "parameter".
Example (Calling a Sub With Parameters)
In the sample program below, as in the previous example, the user is prompted to enter two numbers. The Addem Sub is called, which performs the addition. Program flow then returns to the Console.WriteLine statement, which displays the sum of the two numbers.
The difference this time is that the three variables (the two addends and the sum) are passed as arguments to the Sub Addem. When the call is made, the Sub Addem adds pintNum1 and pintNum2, storing the result in pintSum. When program flow returns to the Console.WriteLine statement, the value of pintSum that was calculated in AddEm is available in its counterpart intSum. (It is important that the keyword ByRef precede pintSum in the header for the Sub Addem procedure. The meaning of ByRef and ByVal is explained a little later below.)
The variables involved are declared at the local level (i.e., in the main method, rather than in the general declarations section). A general rule-of-thumb is that variables should be as limited in scope as possible.
Code:
Module Module1
Sub Main()
'The variables can be declared at the local level
Dim intNum1 As Integer
Dim intNum2 As Integer
Dim intSum As Integer
Console.Write("Enter first number: ")
intNum1 = Val(Console.ReadLine())
Console.Write("Enter second number: ")
intNum2 = Val(Console.ReadLine())
AddEm(intNum1, intNum2, intSum)
Console.WriteLine("The sum is: {0}", intSum)
Console.WriteLine("")
Console.WriteLine("(Press Enter to close this window.)")
Console.ReadLine()
End Sub
Private Sub AddEm(ByVal pintNum1 As Integer, _
ByVal pintNum2 As Integer, _
ByRef pintSum As Integer)
pintSum = pintNum1 + pintNum2
End Sub
End Module
When this example is run, it will behave exactly the same as the previous example.
Download the project code for the example above here.
As indicated in the examples above, variables can be passed to a subroutine "by reference" or "by value", which are specified with the ByRef or ByVal keyword, respectively. The default in VB.NET is "by value". (This is a change from previous versions of VB, where the default was "by reference".)
The differences are as follows:
To call a Function, the syntax is identical to an assignment statement that uses a VB built-in function:
VariableName = FunctionName[(argument list)]
For example, if I made a function called "AddEm" that returned a value (like the sum, for example), I could invoke the function as follows:
intSum = AddEm(intNum1, intNum2)
The above statement would cause the following to happen:
The (simplified) format of the function procedure itself is:
[Private | Public] Function FunctionName[(parameter list)] [As datatype]
[statements]
FunctionName = value
[statements]
End Function
- or -
[Private | Public] Function FunctionName[(parameter list)] [As datatype]
[statements]
Return value
[statements]
End Function
Note that to return a value from a function, you can either assign the value to the function name or include it in a Return statement. When you assign the return value to the function name, any statements following the assignment statement will be executed before returning to the caller. When you assign the return value using the Return statement, control will immediately return to the caller and any remaining statements in the function procedure will be ignored.
Note: In previous versions of VB, the return value of a function could only be set via the first method discussed above (i.e., by assigning the return value to the function name). The Return statement was not used to set the return value of a function (the Return statement exists in previous versions of VB, but its purpose is to return control from a block of code executed by the GoSub statement, which is no longer supported in VB.NET).
As when calling a Sub, the items passed in the argument list of the call to the Function must match the parameter list of the Function header in number and datatypes. The datatype of the return value must match the datatype of the target variable name in the assignment statement that calls the Function.
Following is the code to implement "AddEm" as a function:
Example (Calling a Function)
Module Module1
Sub Main()
'The variables can be declared at the local level
Dim intNum1 As Integer
Dim intNum2 As Integer
Dim intSum As Integer
Console.Write("Enter first number: ")
intNum1 = Val(Console.ReadLine())
Console.Write("Enter second number: ")
intNum2 = Val(Console.ReadLine())
intSum = AddEm(intNum1, intNum2)
Console.WriteLine("The sum is: {0}", intSum)
Console.WriteLine("")
Console.WriteLine("(Press Enter to close this window.)")
Console.ReadLine()
End Sub
Private Function AddEm(ByVal pintNum1 As Integer, _
ByVal pintNum2 As Integer) _
As Integer
Return (pintNum1 + pintNum2)
End Function
End Module
When this example is run, it will behave exactly the same as the previous two examples.
Download the project code for the example above here.
Consider the following function, called IsOdd, which determines whether or not a number is odd:
Private Function IsOdd(ByVal pintNumberIn As Integer) As Boolean
If (pintNumberIn Mod 2) = 0 Then
Return False
Else
Return True
End If
End Function
Now consider the following section of code that calls this function:
Sub Main()
Dim intNumIn As Integer
Console.Write("Enter a number: ")
intNumIn = Val(Console.ReadLine())
If IsOdd(intNumIn) Then
Console.WriteLine("The number that you entered is odd.")
Else
Console.WriteLine("The number that you entered is not odd.")
End If
Console.WriteLine("")
Console.WriteLine("(Press Enter to close this window.)")
Console.ReadLine()
End Sub
Screen-shot:
Download the project code for the example above here.
Using Optional Arguments
VB allows you to create Subs or Functions that have optional arguments – i.e., arguments that may or may not be passed; and if not passed will assume a default value. Optional arguments are specified in the procedure header with keyword Optional. All Optional arguments must be placed at the end of the argument list (they must follow any required arguments). Optional arguments that are not passed will take on default values. The default value of an Optional argument must be set by assigning the optional argument a value in the procedure header. For example, the procedure header
Private Sub MySub(pblnFlag As Boolean, Optional plngNumber As Long = 0)
specifies that the last argument is Optional. Any call to MySub must pass it at least one argument (a Boolean) in order to avoid an error. If the caller so chooses, a second argument (a Long) can also be passed to MySub. Consider the following calls to MySub:
MySub ' Error: Required argument is missing
MySub (True) ' OK – required argument is passed
MySub (False, 10) ' OK – both required and optional arguments passed
In this case, if the argument for plngNumber is not passed, it will have the default value of 0; otherwise, it will have the value of whatever was passed. Default values can only be used with Optional arguments.
Following is an example using a Sub that takes in three Optional arguments with default values. The Sub calculates the gross pay of an employee:
Code:
Module Module1
Sub Main()
Console.WriteLine("Hours".Trim.PadLeft(11) & _
"Base Rate".Trim.PadLeft(11) & _
"O/T Rate".Trim.PadLeft(11) & _
"Base Pay".Trim.PadLeft(11) & _
"O/T Pay".Trim.PadLeft(11) & _
"Gross Pay".Trim.PadLeft(11))
Console.WriteLine("------".Trim.PadLeft(11) & _
"----------".Trim.PadLeft(11) & _
"---------".Trim.PadLeft(11) & _
"---------".Trim.PadLeft(11) & _
"--------".Trim.PadLeft(11) & _
"----------".Trim.PadLeft(11))
CalcGrossPay() ' Default all three arguments
CalcGrossPay(25) ' Default rightmost two arguments
CalcGrossPay(42, 30) ' Default rightmost argument
CalcGrossPay(43, 28, 1.1) ' Pass all three arguments
CalcGrossPay(40, , 1) ' Default second argument
CalcGrossPay(, 37) ' Default first and third arguments
CalcGrossPay(, , 1.25) ' Default first two arguments
Console.WriteLine("")
Console.WriteLine("(Press Enter to close this window.)")
Console.ReadLine()
End Sub
Private Sub CalcGrossPay(Optional ByVal pintHoursWorked As Integer = 40, _
Optional ByVal psngBaseHourlyRate As Single = 35, _
Optional ByVal psngOvertimeRate As Single = 1.5)
Dim sngBasePay As Single
Dim sngOvertimePay As Single
Dim sngGrossPay As Single
If pintHoursWorked <= 40 Then
sngBasePay = psngBaseHourlyRate * pintHoursWorked
sngOvertimePay = 0
Else
sngBasePay = psngBaseHourlyRate * 40
sngOvertimePay = (pintHoursWorked - 40) * psngBaseHourlyRate * psngOvertimeRate
End If
sngGrossPay = sngBasePay + sngOvertimePay
Console.WriteLine(pintHoursWorked.ToString.Trim.PadLeft(11) & _
Format(psngBaseHourlyRate, "Fixed").Trim.PadLeft(11) & _
Format(psngOvertimeRate, "Fixed").Trim.PadLeft(11) & _
Format(sngBasePay, "Currency").Trim.PadLeft(11) & _
Format(sngOvertimePay, "Currency").Trim.PadLeft(11) & _
Format(sngGrossPay, "Currency").Trim.PadLeft(11))
End Sub
End Module
Screen-shot of the run:
Download the project code for the example above here.
The next example demonstrates the use of the IsNothing function, which can be used to test if an Optional Object argument has been passed to a procedure. This example produces the same output as the previous example (however, it is MUCH LESS EFFICIENT because it uses the generic "Object" variable type, rather than specific variable types such as Integer and Single – it is presented here for demonstration purposes only):
Module Module1
Public Sub Main()
Console.WriteLine("Hours".Trim.PadLeft(11) & _
"Base Rate".Trim.PadLeft(11) & _
"O/T Rate".Trim.PadLeft(11) & _
"Base Pay".Trim.PadLeft(11) & _
"O/T Pay".Trim.PadLeft(11) & _
"Gross Pay".Trim.PadLeft(11))
Console.WriteLine("------".Trim.PadLeft(11) & _
"----------".Trim.PadLeft(11) & _
"---------".Trim.PadLeft(11) & _
"---------".Trim.PadLeft(11) & _
"--------".Trim.PadLeft(11) & _
"----------".Trim.PadLeft(11))
CalcGrossPay() ' Default all three arguments
CalcGrossPay(25) ' Default rightmost two arguments
CalcGrossPay(42, 30) ' Default rightmost argument
CalcGrossPay(43, 28, 1.1) ' Pass all three arguments
CalcGrossPay(40, , 1) ' Default second argument
CalcGrossPay(, 37) ' Default first and third arguments
CalcGrossPay(, , 1.25) ' Default first two arguments
Console.WriteLine("")
Console.WriteLine("(Press Enter to close this window.)")
Console.ReadLine()
End Sub
Private Sub CalcGrossPay(Optional ByVal pobjHoursWorked As Object = Nothing, _
Optional ByVal pobjBaseHourlyRate As Object = Nothing, _
Optional ByVal pobjOvertimeRate As Object = Nothing)
Dim sngBasePay As Single
Dim sngOvertimePay As Single
Dim sngGrossPay As Single
If IsNothing(pobjHoursWorked) Then pobjHoursWorked = 40
If IsNothing(pobjBaseHourlyRate) Then pobjBaseHourlyRate = 35
If IsNothing(pobjOvertimeRate) Then pobjOvertimeRate = 1.5
If pobjHoursWorked <= 40 Then
sngBasePay = pobjBaseHourlyRate * pobjHoursWorked
sngOvertimePay = 0
Else
sngBasePay = pobjBaseHourlyRate * 40
sngOvertimePay = (pobjHoursWorked - 40) * pobjBaseHourlyRate * pobjOvertimeRate
End If
sngGrossPay = sngBasePay + sngOvertimePay
Console.WriteLine(pobjHoursWorked.ToString.Trim.PadLeft(11) & _
Format(pobjBaseHourlyRate, "Fixed").Trim.PadLeft(11) & _
Format(pobjOvertimeRate, "Fixed").Trim.PadLeft(11) & _
Format(sngBasePay, "Currency").Trim.PadLeft(11) & _
Format(sngOvertimePay, "Currency").Trim.PadLeft(11) & _
Format(sngGrossPay, "Currency").Trim.PadLeft(11))
End Sub
End Module
Note that data of any type can be assigned to an Object variable; in the procedure above, the default numeric values are assigned to the optional Object variables. In previous versions of VB, the Variant data type was used to hold any type of value; in VB.NET, the Variant data type is not supported, but the Object data type assumes this functionality. In addition, previous versions of VB used the IsMissing function to determine whether or not an optional Variant argument was passed to a procedure; in VB.NET, the IsMissing function is not supported, but the IsNothing function assumes the functionality for testing whether or not an optional Object argument has been passed to a procedure.
Download the project code for the example above here.
The following example demonstrates the use of the named arguments, where you can specify the arguments to be passed in the calling statement using the syntax
ArgumentName:=value
Using named arguments, the arguments to the procedure can be passed in any order. All required arguments must be passed, but optional arguments can be omitted if desired. Using named arguments saves coding when calling a procedure that has a lot of optional arguments but you only need to pass a few of them.
Following is the code for GrossPay program using named arguments:
Module Module1
Sub Main()
Console.WriteLine("Hours".Trim.PadLeft(11) & _
"Base Rate".Trim.PadLeft(11) & _
"O/T Rate".Trim.PadLeft(11) & _
"Base Pay".Trim.PadLeft(11) & _
"O/T Pay".Trim.PadLeft(11) & _
"Gross Pay".Trim.PadLeft(11))
Console.WriteLine("------".Trim.PadLeft(11) & _
"----------".Trim.PadLeft(11) & _
"---------".Trim.PadLeft(11) & _
"---------".Trim.PadLeft(11) & _
"--------".Trim.PadLeft(11) & _
"----------".Trim.PadLeft(11))
CalcGrossPay()
CalcGrossPay(pintHoursWorked:=25)
CalcGrossPay(pintHoursWorked:=42, psngBaseHourlyRate:=30)
CalcGrossPay(pintHoursWorked:=43, psngBaseHourlyRate:=28, psngOvertimeRate:=1.1)
CalcGrossPay(pintHoursWorked:=40, psngOvertimeRate:=1)
CalcGrossPay(psngBaseHourlyRate:=37)
CalcGrossPay(psngOvertimeRate:=1.25)
Console.WriteLine("")
Console.WriteLine("(Press Enter to close this window.)")
Console.ReadLine()
End Sub
Private Sub CalcGrossPay(Optional ByVal pintHoursWorked As Integer = 40, _
Optional ByVal psngBaseHourlyRate As Single = 35, _
Optional ByVal psngOvertimeRate As Single = 1.5)
Dim sngBasePay As Single
Dim sngOvertimePay As Single
Dim sngGrossPay As Single
If pintHoursWorked <= 40 Then
sngBasePay = psngBaseHourlyRate * pintHoursWorked
sngOvertimePay = 0
Else
sngBasePay = psngBaseHourlyRate * 40
sngOvertimePay = (pintHoursWorked - 40) * psngBaseHourlyRate * psngOvertimeRate
End If
sngGrossPay = sngBasePay + sngOvertimePay
Console.WriteLine(pintHoursWorked.ToString.Trim.PadLeft(11) & _
Format(psngBaseHourlyRate, "Fixed").Trim.PadLeft(11) & _
Format(psngOvertimeRate, "Fixed").Trim.PadLeft(11) & _
Format(sngBasePay, "Currency").Trim.PadLeft(11) & _
Format(sngOvertimePay, "Currency").Trim.PadLeft(11) & _
Format(sngGrossPay, "Currency").Trim.PadLeft(11))
End Sub
End Module
Download the project code for the example above here.
If you need to exit a Sub or Function procedure "early", you can use the Exit Sub or Exit Function statements, respectively.
Example:
Dim intNumIn As Integer
Console.WriteLine("Enter a non-zero number: ")
intNumIn = Val(Console.ReadLine())
If intNumIn = 0 Then
Console.WriteLine ("You entered zero or a non-numeric value.")
Exit Sub
End If
' processing will continue here only if a non-zero number was entered
Console.WriteLine "The number you entered was: " & intNumIn
' do some other stuff