Passing Arguments
to Procedures   


An argument is a piece of data passed from the calling routine to a procedure.

Passing Constant Literals

Private Sub cmdTest_Click(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles cmdTest.Click
     Call repeatChars ("+", 10)
     Call repeatChars ("=", 15)
End Sub

Private Sub repeatChars (ByVal character as String, ByVal count as Integer)
     Dim lcv As Integer
     For lcv = 1 to count
          Console.Write(character)
     Next lcv
     Console.WriteLine()
End Sub

  • The values supplied in the call must be of the types specified in the declaration: the first argument must be of type String and the second argument must be of type Integer.
  • The types in the declaration and the definition must also agree, and there must be the same number.
  • The calling program supplies arguments to the procedure. The variables used within the procedure to hold the argument values are called parameters.
  • The declarator in the procedure definition specifies both the data types and the names of the parameters:
  • These parameter names are used in the procedure as if they were normal variables.
  • When the procedure is called, its parameters are automatically initialized to the values passed by the calling program.

Passing Variables

Variables can also be passed as arguments, as seen in the following code segment.

Dim characterIn As String
Dim count As Integer
characterIn = "@"
count = 24

Call repeatChars (characterIn, count)


Call-by-Value vs. Call-by-Reference

There are two ways to pass parameters to procedures: call-by-value and call-by-reference.

Call-by-value

  • the default for argument passing.
  • The procedure creates new variables to hold the values of the arguments. The procedure gives these new variables the names and data types of the parameters specified in the declarator: character of type String and number of type Integer. It initializes these parameters to the values passed, and they can then be accessed like other variables by statements in the procedure body.
  • passes a copy of the argument's value to the called routine, and that copy can be manipulated without affecting the original value in the calling routine.
  • This makes it possible to modify the values in the procedure without affecting the original variables. This offers insurance that the procedure cannot harm the original variable by using a copy of the original variable.
  • An argument can be passed call-by-value either by using keyword ByVal in the declaration or by enclosing that argument in the call in parentheses, ( ).  Placing the argument in its own set of parentheses forces evaluation of it as an expression.
  • The parenthesis in the call and keyword ByVal in the declaration can be used together or separately.  
  • Be sure all parameters in a function are passed ByVal.

________________

Call-by-reference

  • provides a mechanism for returning more than one value from the procedure back to the calling program.
  • Passing arguments by reference passes a reference to the original variable (in the calling program)
  • allows the called procedure to directly access the caller's data, and to modify that data if the called procedure so chooses.
  • The primary advantage/drawback of passing by reference is that the procedure can access the actual variables in the calling program.
    • This is dangerous because the called procedure can alter the original parameter, possibly resulting in hard-to-detect side effects.

________________

The following header declares two variables:
        Private Sub TestIt ( ByVal var1 As Long, ByRef var2 As Boolean ) 

TestIt receives var1 by value and var2 by reference.

TestIt could be rewritten as
        Private Sub TestIt ( var1 As Long, ByRef var2 As Boolean )

The call
        Call TestIt ( (passACopy), passOriginal )
passes passACopy by value and passOriginal by reference.


Passing Simple Data Types by Reference

Private Sub cmdTest_Click(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles cmdTest.Click
     Dim targetNumber As Single
     Dim integerPortion As Single = 0
     Dim fractionalPortion As Single = 0

     targetNumber = 3.14159
     Call
integerFraction (targetNumber, integerPortion, fractionalPortion)
     Console.WriteLine("Integer portion is " & integerPortion)
     Console.WriteLine("Fractional portion is " & fractionalPortion)

End Sub

Private Sub integerFraction (ByVal number As Single, _
                                         integerPart As Single, _
                                         fractionalPart As Single)
     
integerPart = Int (number)
      fractionalPart = number - integerPart
End Sub

Private Sub integerFraction (ByVal number As Single, _
                                         ByRef integerPart As Single, _
                                         ByRef fractionalPart As Single)
      integerPart = Int (number)
      fractionalPart = number - integerPart
End Sub

The following program demonstrates call-by-value and call-by-reference.

The program contains three procedures that receive arguments call-by-value and two procedures that receive an argument call-by-reference.

' ByRefTest.vb -- Demonstrates passing by reference.

Module modByRefTest

     ' squares three values ByVal and ByRef, displays results
     Sub Main()
          Dim number1 As Integer = 2

          Console.WriteLine("Passing a value-type argument by value:")
          Console.WriteLine("Before calling SquareByValue, " & _
               "number1 is {0}", number1)
          SquareByValue(number1) ' passes number1 by value
          Console.WriteLine("After returning from SquareByValue, " & _
               "number1 is {0}" & vbCrLf, number1)

          Dim number2 As Integer = 2

          Console.WriteLine("Passing a value-type argument" & _
               " by reference:")
          Console.WriteLine("Before calling SquareByReference, " & _
               "number2 is {0}", number2)
          SquareByReference(number2) ' passes number2 by reference
          Console.WriteLine("After returning from " & _
               "SquareByReference, number2 is {0}" & vbCrLf, number2)

          Dim number3 As Integer = 2

          Console.WriteLine("Passing a value-type argument" & _
               " by reference, but in parentheses:")
          Console.WriteLine("Before calling SquareByReference " & _
               "using parentheses, number3 is {0}", number3)
          SquareByReference((number3)) ' passes number3 by value
          Console.WriteLine("After returning from " & _
          "SquareByReference, number3 is {0}", number3)

     End Sub ' Main

     ' squares number by value (note ByVal keyword)
     Sub SquareByValue(ByVal number As Integer)
          Console.WriteLine("After entering SquareByValue, " & _
               "number is {0}", number)
          number *= number
          Console.WriteLine("Before exiting SquareByValue, " & _
               "number is {0}", number)
     End Sub ' SquareByValue

     ' squares number by reference (note ByRef keyword)
     Sub SquareByReference(ByRef number As Integer)
          Console.WriteLine("After entering SquareByReference" & _
               ", number is {0}", number)
          number *= number
          Console.WriteLine("Before exiting SquareByReference" & _
               ", number is {0}", number)
     End Sub ' SquareByReference

End Module ' modByRefTest

  Call-By-Value Call-By-Reference
Parameter Use Manipulate copy of data Manipulate original data
Keyword in heading ByVal ByRef
Keyword in call ( )  
Default Yes No
Dangerous No Yes
Can "return" value No Yes

Coercion

  • Another important feature of procedure definitions is the coercion of arguments, i.e., the forcing of arguments to the appropriate type to pass to a procedure.

  • Widening conversions occur when a type is converted to another type (usually one that can represent larger values) without losing data.  One example is the conversion of an Integer to a Long or Decimal.

  • Narrowing conversions occur when there is potential for data loss during conversion (usually to a type that holds a smaller amount of data).  One example is the conversion of a Double to a Single or an Integer.

  • The Math class method Sqrt can be called with an Integer argument even though the method is defined in the Math class to receive a Double argument, and the method will still work correctly. The statement 
                Console.WriteLine( Math.sqrt( 4 ) ); 
    correctly evaluates Math.sqrt( 4 ), and prints the value 2. 

  • The method definition's parameter list causes the Integer value 4 to be converted or promoted to the Double value 4.0 before the value is passed to Math.Sqrt. 

  • In cases like this, argument values that do not correspond precisely to the parameter types in the method definition are converted to the proper type through an implicit widening conversion before the method is called. 

  • VB also performs narrowing conversions on arguments passed to procedures.  For example, if a textbox contains the String value "4", the method call Math.Sqrt(txtNum.Text)) correctly evaluates to 2.

  • In some cases, these conversions can lead to compiler errors if the promotion rules are not followed. The promotion rules specify how types can be converted to other types without losing data. 

  • In the Math.Sqrt example above, an Integer is automatically converted to a Double without changing its value. However, a Double converted to an Integer truncates the fractional part of the Double value. Converting large integer types to small integer types (e.g., Long to Integer) may also result in changed values. 

  • The promotion rules apply to expressions containing values of two or more data types; such expressions are also referred to as mixed-type expressions. The type of each value in a mixed-type expression is promoted to the ''highest'' type in the expression.

  • Converting values to lower types can result in incorrect values. Therefore you can set an option, Option Strict to ON, so that in cases where information may be lost due to conversion the compiler requires the programmer to use a cast operator to force the conversion to occur. 

  • To convert a Double to an Integer you can use
        intNum = CInt(doubNum) or
        intNum = Convert.ToInt32(doubNum)
    These statements explicitly convert the value of doubNum to an Integer.