Sideway
output.to from Sideway
Draft for Information Only

Content

VB.NET Procedure Characteristics
  Procedure Parameters and Arguments
 Parameter Data Type
  Type Parameters
 See also
  Differences Between Parameters and Arguments
 Parameters
 Arguments
 See also
  How to: Define a Parameter for a Procedure
  To define a procedure parameter
 See also
  How to: Pass Arguments to a Procedure
  To pass one or more arguments to a procedure
 See also
  Passing Arguments by Value and by Reference
 Distinctions
 Choice of Passing Mechanism
 Determination of the Passing Mechanism
 When to Pass an Argument by Value
 When to Pass an Argument by Reference
 Example
  Description
  Code
 See also
  Differences Between Modifiable and Nonmodifiable Arguments
 Modifiable and Nonmodifiable Elements
 Modifiable and Nonmodifiable Arguments
 See also
  Differences Between Passing an Argument By Value and By Reference
 Passing by Value
 Passing by Reference
 Passing Mechanism and Element Type
 Ability to Modify
 See also
  How to: Change the Value of a Procedure Argument
 Changing the Underlying Value
   To change the underlying value of a procedure argument in the calling code
 Changing Local Copies
   To change the copy of a procedure argument in the procedure code
 Example
 Compiling the Code
 .NET Framework Security
 See also
  How to: Protect a Procedure Argument Against Value Changes
 Example
 Compiling the Code
 See also
  How to: Force an Argument to Be Passed by Value
  To force an argument to be passed by value
 Example
 Compiling the Code
 Robust Programming
 .NET Framework Security
 See also
  Passing Arguments by Position and by Name
 Passing Arguments by Position
 Passing Arguments by Name
 Mixing Arguments by Position and by Name
 Restrictions on Supplying Arguments by Name
 See also
  Optional Parameters
 Calling Procedures with Optional Parameters
 Determining Whether an Optional Argument Is Present
 Optional Parameters and Overloading
 See also
  Parameter Arrays
 Declaring a ParamArray
 Calling a ParamArray
 Example
 See also
  Support for reference return values
 Modifying the ref return value directly
 Using a helper method
 See also
  Recursive Procedures
 Considerations with Recursive Procedures
 See also
  Procedure Overloading
 Overloading Rules
 Multiple Versions of a Procedure
  Overloaded Versions
   Additional Overloads
 Advantages of Overloading
 See also
  How to: Define Multiple Versions of a Procedure
  To define multiple versions of a procedure
 Example
 Compiling the Code
 See also
  How to: Call an Overloaded Procedure
  To call a procedure that has more than one version defined
 See also
  How to: Overload a Procedure that Takes Optional Parameters
 One Optional Parameter
   To overload a procedure that takes one optional parameter
 Multiple Optional Parameters
   To overload a procedure that takes more than one optional parameter
 See also
  How to: Overload a Procedure that Takes an Indefinite Number of Parameters
  To overload a procedure that takes a variable number of parameters
 Example
 Compiling the Code
 .NET Framework Security
 See also
  Considerations in Overloading Procedures
 Alternatives to Overloaded Versions
  Overloads and Optional Arguments
   When to Use Overloaded Versions
   When to Use Optional Parameters
  Overloads and ParamArrays
   When to Use Overloaded Versions
   When to Use a Parameter Array
 Implicit Overloads for Optional Parameters
 Implicit Overloads for a ParamArray Parameter
 Typeless Programming as an Alternative to Overloading
 See also
  Overload Resolution
 Overloaded Optional and ParamArray Arguments
 See also
  Troubleshooting Procedures
 Returning an Array Type from a Function Procedure
 Argument Not Being Modified by Procedure Call
 Unable to Define an Overload
  Overload Resolution with Optional and ParamArray Arguments
 Calling a Wrong Version of an Overloaded Procedure
  Overload Resolution Failure
  Overload Resolution with Optional and ParamArray Arguments
 See also
  Extension Methods
 Remarks
 Example
  Description
  Code
  Comments
 Types That Can Be Extended
 Best Practices
 Extension Methods, Instance Methods, and Properties
 Extension Method Precedence
 See also
  How to: Write an Extension Method
  To define an extension method
 Example
 See also
  How to: Call an Extension Method
  To call an extension method
 Example
 See also
  Lambda Expressions
 Lambda Expression Syntax
 Async Lambdas
 Context
 Converting to a Delegate Type
 Examples
 See also
  How to: Create a Lambda Expression
  To create a single-line lambda expression function
  To create a single-line lambda expression subroutine
  To create a multiline lambda expression function
  To create a multiline lambda expression subroutine
 Example
 See also
  Partial Methods
 Declaration
 Implementation
 Use
 Example
 See also
  Source/Reference

VB.NET Procedure Characteristics

Procedure Parameters and Arguments

In most cases, a procedure needs some information about the circumstances in which it has been called. A procedure that performs repeated or shared tasks uses different information for each call. This information consists of variables, constants, and expressions that you pass to the procedure when you call it.

A parameter represents a value that the procedure expects you to supply when you call it. The procedure's declaration defines its parameters.

You can define a procedure with no parameters, one parameter, or more than one. The part of the procedure definition that specifies the parameters is called the parameter list.

An argument represents the value you supply to a procedure parameter when you call the procedure. The calling code supplies the arguments when it calls the procedure. The part of the procedure call that specifies the arguments is called the argument list.

The following illustration shows code calling the procedure safeSquareRoot from two different places. The first call passes the value of the variable x (4.0) to the parameter number, and the return value in root (2.0) is assigned to the variable y. The second call passes the literal value 9.0 to number, and assigns the return value (3.0) to variable z.

Diagram that shows passing an argument to a parameter

For more information, see Differences Between Parameters and Arguments.

Parameter Data Type

You define a data type for a parameter by using the As clause in its declaration. For example, the following function accepts a string and an integer.

VB
Function appointment(ByVal day As String, ByVal hour As Integer) As String
    ' Insert code to return any appointment for the given day and time.
    Return "appointment"
End Function

If the type checking switch (Option Strict Statement) is Off, the As clause is optional, except that if any one parameter uses it, all parameters must use it. If type checking is On, the As clause is required for all procedure parameters.

If the calling code expects to supply an argument with a data type different from that of its corresponding parameter, such as Byte to a String parameter, it must do one of the following:

  • Supply only arguments with data types that widen to the parameter data type;

  • Set Option Strict Off to allow implicit narrowing conversions; or

  • Use a conversion keyword to explicitly convert the data type.

Type Parameters

A generic procedure also defines one or more type parameters in addition to its normal parameters. A generic procedure allows the calling code to pass different data types each time it calls the procedure, so it can tailor the data types to the requirements of each individual call. See Generic Procedures in Visual Basic.

See also

Differences Between Parameters and Arguments

In most cases, a procedure must have some information about the circumstances in which it has been called. A procedure that performs repeated or shared tasks uses different information for each call. This information consists of variables, constants, and expressions that you pass to the procedure when you call it.

To communicate this information to the procedure, the procedure defines a parameter, and the calling code passes an argument to that parameter. You can think of the parameter as a parking space and the argument as an automobile. Just as different automobiles can park in a parking space at different times, the calling code can pass a different argument to the same parameter every time that it calls the procedure.

Parameters

A parameter represents a value that the procedure expects you to pass when you call it. The procedure's declaration defines its parameters.

When you define a Function or Sub procedure, you specify a parameter list in parentheses immediately following the procedure name. For each parameter, you specify a name, a data type, and a passing mechanism (ByVal or ByRef). You can also indicate that a parameter is optional. This means that the calling code does not have to pass a value for it.

The name of each parameter serves as a local variable in the procedure. You use the parameter name the same way you use any other variable.

Arguments

An argument represents the value that you pass to a procedure parameter when you call the procedure. The calling code supplies the arguments when it calls the procedure.

When you call a Function or Sub procedure, you include an argument list in parentheses immediately following the procedure name. Each argument corresponds to the parameter in the same position in the list.

In contrast to parameter definition, arguments do not have names. Each argument is an expression, which can contain zero or more variables, constants, and literals. The data type of the evaluated expression should typically match the data type defined for the corresponding parameter, and in any case it must be convertible to the parameter type.

See also

How to: Define a Parameter for a Procedure

A parameter allows the calling code to pass a value to the procedure when it calls it. You declare each parameter for a procedure the same way you declare a variable, specifying its name and data type. You also specify the passing mechanism, and whether the parameter is optional.

For more information, see Procedure Parameters and Arguments.

To define a procedure parameter

  1. In the procedure declaration, add the parameter name to the procedure's parameter list, separating it from other parameters by commas.

  2. Decide the data type of the parameter.

  3. Follow the parameter name with an As clause to specify the data type.

  4. Decide the passing mechanism you want for the parameter. Normally you pass a parameter by value, unless you want the procedure to be able to change its value in the calling code.

  5. Precede the parameter name with ByVal or ByRef to specify the passing mechanism. For more information, see Differences Between Passing an Argument By Value and By Reference.

  6. If the parameter is optional, precede the passing mechanism with Optional and follow the parameter data type with an equal sign (=) and a default value.

    The following example defines the outline of a Sub procedure with three parameters. The first two are required and the third is optional. The parameter declarations are separated in the parameter list by commas.

    VB
  1. Sub updateCustomer(ByRef c As customer, ByVal region As String, 
      Optional ByVal level As Integer = 0)
      ' Insert code to update a customer object.
    End Sub
    

    The first parameter accepts a customer object, and updateCustomer can directly update the variable passed to c because the argument is passed ByRef. The procedure cannot change the values of the last two arguments because they are passed ByVal.

    If the calling code does not supply a value for the level parameter, Visual Basic sets it to the default value of 0.

    If the type checking switch (Option Strict Statement) is Off, the As clause is optional when you define a parameter. However, if any one parameter uses an As clause, all of them must use it. If the type checking switch is On, the As clause is required for every parameter definition.

    Specifying data types for all your programming elements is known as strong typing. When you set Option Strict On, Visual Basic enforces strong typing. This is strongly recommended, for the following reasons:

    • It enables IntelliSense support for your variables and parameters. This allows you to see their properties and other members as you type in your code.

    • It allows the compiler to perform type checking. This helps catch statements that can fail at run time due to errors such as overflow. It also catches calls to methods on objects that do not support them.

    • It results in faster execution of your code. One reason for this is that if you do not specify a data type for a programming element, the Visual Basic compiler assigns it the Object type. Your compiled code might have to convert back and forth between Object and other data types, which reduces performance.

See also

How to: Pass Arguments to a Procedure

When you call a procedure, you follow the procedure name with an argument list in parentheses. You supply an argument corresponding to every required parameter the procedure defines, and you can optionally supply arguments to the Optional parameters. If you do not supply an Optional parameter in the call, you must include a comma to mark its place in the argument list if you are supplying any subsequent arguments.

If you intend to pass an argument of a data type different from that of its corresponding parameter, such as Byte to String, you can set the type-checking switch (Option Strict Statement) to Off. If Option Strict is On, you must use either widening conversions or explicit conversion keywords. For more information, see Widening and Narrowing Conversions and Type Conversion Functions.

For more information, see Procedure Parameters and Arguments.

To pass one or more arguments to a procedure

  1. In the calling statement, follow the procedure name with parentheses.

  2. Inside the parentheses, put an argument list. Include an argument for each required parameter the procedure defines, and separate the arguments with commas.

  3. Make sure each argument is a valid expression that evaluates to a data type convertible to the type the procedure defines for the corresponding parameter.

  4. If a parameter is defined as Optional, you can either include it in the argument list or omit it. If you omit it, the procedure uses the default value defined for that parameter.

  5. If you omit an argument for an Optional parameter and there is another parameter after it in the parameter list, you can mark the place of the omitted argument by an extra comma in the argument list.

    The following example calls the Visual Basic MsgBox function.

    VB
  1. Dim mbResult As MsgBoxResult
    Dim displayString As String = "Show this string to the user"
    mbResult = MsgBox(displayString, , "Put this in the title bar")
    

    The preceding example supplies the required first argument, which is the message string to be displayed. It omits an argument for the optional second parameter, which specifies the buttons to be displayed on the message box. Because the call does not supply a value, MsgBox uses the default value, MsgBoxStyle.OKOnly, which displays only an OK button.

    The second comma in the argument list marks the place of the omitted second argument, and the last string is passed to the optional third parameter of MsgBox, which is the text to be displayed in the title bar.

See also

Passing Arguments by Value and by Reference

In Visual Basic, you can pass an argument to a procedure by value or by reference. This is known as the passing mechanism, and it determines whether the procedure can modify the programming element underlying the argument in the calling code. The procedure declaration determines the passing mechanism for each parameter by specifying the ByVal or ByRef keyword.

Distinctions

When passing an argument to a procedure, be aware of several different distinctions that interact with each other:

  • Whether the underlying programming element is modifiable or nonmodifiable

  • Whether the argument itself is modifiable or nonmodifiable

  • Whether the argument is being passed by value or by reference

  • Whether the argument data type is a value type or a reference type

For more information, see Differences Between Modifiable and Nonmodifiable Arguments and Differences Between Passing an Argument By Value and By Reference.

Choice of Passing Mechanism

You should choose the passing mechanism carefully for each argument.

  • Protection. In choosing between the two passing mechanisms, the most important criterion is the exposure of calling variables to change. The advantage of passing an argument ByRef is that the procedure can return a value to the calling code through that argument. The advantage of passing an argument ByVal is that it protects a variable from being changed by the procedure.

  • Performance. Although the passing mechanism can affect the performance of your code, the difference is usually insignificant. One exception to this is a value type passed ByVal. In this case, Visual Basic copies the entire data contents of the argument. Therefore, for a large value type such as a structure, it can be more efficient to pass it ByRef.

    For reference types, only the pointer to the data is copied (four bytes on 32-bit platforms, eight bytes on 64-bit platforms). Therefore, you can pass arguments of type String or Object by value without harming performance.

Determination of the Passing Mechanism

The procedure declaration specifies the passing mechanism for each parameter. The calling code can't override a ByVal mechanism.

If a parameter is declared with ByRef, the calling code can force the mechanism to ByVal by enclosing the argument name in parentheses in the call. For more information, see How to: Force an Argument to Be Passed by Value.

The default in Visual Basic is to pass arguments by value.

When to Pass an Argument by Value

  • If the calling code element underlying the argument is a nonmodifiable element, declare the corresponding parameter ByVal. No code can change the value of a nonmodifiable element.

  • If the underlying element is modifiable, but you do not want the procedure to be able to change its value, declare the parameter ByVal. Only the calling code can change the value of a modifiable element passed by value.

When to Pass an Argument by Reference

  • If the procedure has a genuine need to change the underlying element in the calling code, declare the corresponding parameter ByRef.

  • If the correct execution of the code depends on the procedure changing the underlying element in the calling code, declare the parameter ByRef. If you pass it by value, or if the calling code overrides the ByRef passing mechanism by enclosing the argument in parentheses, the procedure call might produce unexpected results.

Example

Description

The following example illustrates when to pass arguments by value and when to pass them by reference. Procedure Calculate has both a ByVal and a ByRef parameter. Given an interest rate, rate, and a sum of money, debt, the task of the procedure is to calculate a new value for debt that is the result of applying the interest rate to the original value of debt. Because debt is a ByRef parameter, the new total is reflected in the value of the argument in the calling code that corresponds to debt. Parameter rate is a ByVal parameter because Calculate should not change its value.

Code

VB
Module Module1

    Sub Main()
        ' Two interest rates are declared, one a constant and one a 
        ' variable.
        Const highRate As Double = 12.5
        Dim lowRate = highRate * 0.6

        Dim initialDebt = 4999.99
        ' Make a copy of the original value of the debt.
        Dim debtWithInterest = initialDebt

        ' Calculate the total debt with the high interest rate applied.
        ' Argument highRate is a constant, which is appropriate for a 
        ' ByVal parameter. Argument debtWithInterest must be a variable
        ' because the procedure will change its value to the calculated
        ' total with interest applied.
        Calculate(highRate, debtWithInterest)
        ' Format the result to represent currency, and display it.
        Dim debtString = Format(debtWithInterest, "C")
        Console.WriteLine("What I owe with high interest: " & debtString)

        ' Repeat the process with lowRate. Argument lowRate is not a 
        ' constant, but the ByVal parameter protects it from accidental
        ' or intentional change by the procedure. 

        ' Set debtWithInterest back to the original value.
        debtWithInterest = initialDebt
        Calculate(lowRate, debtWithInterest)
        debtString = Format(debtWithInterest, "C")
        Console.WriteLine("What I owe with low interest:  " & debtString)
    End Sub

    ' Parameter rate is a ByVal parameter because the procedure should
    ' not change the value of the corresponding argument in the 
    ' calling code. 

    ' The calculated value of the debt parameter, however, should be
    ' reflected in the value of the corresponding argument in the 
    ' calling code. Therefore, it must be declared ByRef. 
    Sub Calculate(ByVal rate As Double, ByRef debt As Double)
        debt = debt + (debt * rate / 100)
    End Sub

End Module

See also

Differences Between Modifiable and Nonmodifiable Arguments

When you call a procedure, you typically pass one or more arguments to it. Each argument corresponds to an underlying programming element. Both the underlying elements and the arguments themselves can be either modifiable or nonmodifiable.

Modifiable and Nonmodifiable Elements

A programming element can be either a modifiable element, which can have its value changed, or a nonmodifiable element, which has a fixed value once it has been created.

The following table lists modifiable and nonmodifiable programming elements.

Modifiable elements Nonmodifiable elements
Local variables (declared inside procedures), including object variables, except for read-only Read-only variables, fields, and properties
Fields (member variables of modules, classes, and structures), except for read-only Constants and literals
Properties, except for read-only Enumeration members
Array elements Expressions (even if their elements are modifiable)

Modifiable and Nonmodifiable Arguments

A modifiable argument is one with a modifiable underlying element. The calling code can store a new value at any time, and if you pass the argument ByRef, the code in the procedure can also modify the underlying element in the calling code.

A nonmodifiable argument either has a nonmodifiable underlying element or is passed ByVal. The procedure cannot modify the underlying element in the calling code, even if it is a modifiable element. If it is a nonmodifiable element, the calling code itself cannot modify it.

The called procedure might modify its local copy of a nonmodifiable argument, but that modification does not affect the underlying element in the calling code.

See also

Differences Between Passing an Argument By Value and By Reference

When you pass one or more arguments to a procedure, each argument corresponds to an underlying programming element in the calling code. You can pass either the value of this underlying element, or a reference to it. This is known as the passing mechanism.

Passing by Value

You pass an argument by value by specifying the ByVal keyword for the corresponding parameter in the procedure definition. When you use this passing mechanism, Visual Basic copies the value of the underlying programming element into a local variable in the procedure. The procedure code does not have any access to the underlying element in the calling code.

Passing by Reference

You pass an argument by reference by specifying the ByRef keyword for the corresponding parameter in the procedure definition. When you use this passing mechanism, Visual Basic gives the procedure a direct reference to the underlying programming element in the calling code.

Passing Mechanism and Element Type

The choice of passing mechanism is not the same as the classification of the underlying element type. Passing by value or by reference refers to what Visual Basic supplies to the procedure code. A value type or reference type refers to how a programming element is stored in memory.

However, the passing mechanism and element type are interrelated. The value of a reference type is a pointer to the data elsewhere in memory. This means that when you pass a reference type by value, the procedure code has a pointer to the underlying element's data, even though it cannot access the underlying element itself. For example, if the element is an array variable, the procedure code does not have access to the variable itself, but it can access the array members.

Ability to Modify

When you pass a nonmodifiable element as an argument, the procedure can never modify it in the calling code, whether it is passed ByVal or ByRef.

For a modifiable element, the following table summarizes the interaction between the element type and the passing mechanism.

Element type Passed ByVal Passed ByRef
Value type (contains only a value) The procedure cannot change the variable or any of its members. The procedure can change the variable and its members.
Reference type (contains a pointer to a class or structure instance) The procedure cannot change the variable but can change members of the instance to which it points. The procedure can change the variable and members of the instance to which it points.

See also

How to: Change the Value of a Procedure Argument

When you call a procedure, each argument you supply corresponds to one of the parameters defined in the procedure. In some cases, the procedure code can change the value underlying an argument in the calling code. In other cases, the procedure can change only its local copy of an argument.

When you call the procedure, Visual Basic makes a local copy of every argument that is passed ByVal. For each argument passed ByRef, Visual Basic gives the procedure code a direct reference to the programming element underlying the argument in the calling code.

If the underlying element in the calling code is a modifiable element and the argument is passed ByRef, the procedure code can use the direct reference to change the element's value in the calling code.

Changing the Underlying Value

To change the underlying value of a procedure argument in the calling code

  1. In the procedure declaration, specify ByRef for the parameter corresponding to the argument.

  2. In the calling code, pass a modifiable programming element as the argument.

  3. In the calling code, do not enclose the argument in parentheses in the argument list.

  4. In the procedure code, use the parameter name to assign a value to the underlying element in the calling code.

See the example further down for a demonstration.

Changing Local Copies

If the underlying element in the calling code is a nonmodifiable element, or if the argument is passed ByVal, the procedure cannot change its value in the calling code. However, the procedure can change its local copy of such an argument.

To change the copy of a procedure argument in the procedure code

  1. In the procedure declaration, specify ByVal for the parameter corresponding to the argument.

    -or-

    In the calling code, enclose the argument in parentheses in the argument list. This forces Visual Basic to pass the argument by value, even if the corresponding parameter specifies ByRef.

  2. In the procedure code, use the parameter name to assign a value to the local copy of the argument. The underlying value in the calling code is not changed.

Example

The following example shows two procedures that take an array variable and operate on its elements. The increase procedure simply adds one to each element. The replace procedure assigns a new array to the parameter a() and then adds one to each element.

VB
Public Sub increase(ByVal a() As Long)
    For j As Integer = 0 To UBound(a)
        a(j) = a(j) + 1
    Next j
End Sub
VB
Public Sub replace(ByRef a() As Long)
    Dim k() As Long = {100, 200, 300}
    a = k
    For j As Integer = 0 To UBound(a)
        a(j) = a(j) + 1
    Next j
End Sub
VB
Dim n() As Long = {10, 20, 30, 40}
Call increase(n)
MsgBox("After increase(n): " & CStr(n(0)) & ", " & 
    CStr(n(1)) & ", " & CStr(n(2)) & ", " & CStr(n(3)))
Call replace(n)
MsgBox("After replace(n): " & CStr(n(0)) & ", " & 
    CStr(n(1)) & ", " & CStr(n(2)) & ", " & CStr(n(3)))

The first MsgBox call displays "After increase(n): 11, 21, 31, 41". Because the array n is a reference type, replace can change its members, even though the passing mechanism is ByVal.

The second MsgBox call displays "After replace(n): 101, 201, 301". Because n is passed ByRef, replace can modify the variable n in the calling code and assign a new array to it. Because n is a reference type, replace can also change its members.

You can prevent the procedure from modifying the variable itself in the calling code. See How to: Protect a Procedure Argument Against Value Changes.

Compiling the Code

When you pass a variable by reference, you must use the ByRef keyword to specify this mechanism.

The default in Visual Basic is to pass arguments by value. However, it is good programming practice to include either the ByVal or ByRef keyword with every declared parameter. This makes your code easier to read.

.NET Framework Security

There is always a potential risk in allowing a procedure to change the value underlying an argument in the calling code. Make sure you expect this value to be changed, and be prepared to check it for validity before using it.

See also

How to: Protect a Procedure Argument Against Value Changes

If a procedure declares a parameter as ByRef, Visual Basic gives the procedure code a direct reference to the programming element underlying the argument in the calling code. This permits the procedure to change the value underlying the argument in the calling code. In some cases the calling code might want to protect against such a change.

You can always protect an argument from change by declaring the corresponding parameter ByVal in the procedure. If you want to be able to change a given argument in some cases but not others, you can declare it ByRef and let the calling code determine the passing mechanism in each call. It does this by enclosing the corresponding argument in parentheses to pass it by value, or not enclosing it in parentheses to pass it by reference. For more information, see How to: Force an Argument to Be Passed by Value.

Example

The following example shows two procedures that take an array variable and operate on its elements. The increase procedure simply adds one to each element. The replace procedure assigns a new array to the parameter a() and then adds one to each element. However, the reassignment does not affect the underlying array variable in the calling code.

VB
Public Sub increase(ByVal a() As Long)
    For j As Integer = 0 To UBound(a)
        a(j) = a(j) + 1
    Next j
End Sub
VB
Public Sub replace(ByVal a() As Long)
    Dim k() As Long = {100, 200, 300}
    a = k
    For j As Integer = 0 To UBound(a)
        a(j) = a(j) + 1
    Next j
End Sub
VB
Dim n() As Long = {10, 20, 30, 40}
Call increase(n)
MsgBox("After increase(n): " & CStr(n(0)) & ", " & 
    CStr(n(1)) & ", " & CStr(n(2)) & ", " & CStr(n(3)))
Call replace(n)
MsgBox("After replace(n): " & CStr(n(0)) & ", " & 
    CStr(n(1)) & ", " & CStr(n(2)) & ", " & CStr(n(3)))

The first MsgBox call displays "After increase(n): 11, 21, 31, 41". Because the array n is a reference type, increase can change its members, even though the passing mechanism is ByVal.

The second MsgBox call displays "After replace(n): 11, 21, 31, 41". Because n is passed ByVal, replace cannot modify the variable n in the calling code by assigning a new array to it. When replace creates the new array instance k and assigns it to the local variable a, it loses the reference to n passed in by the calling code. When it changes the members of a, only the local array k is affected. Therefore, replace does not increment the values of array n in the calling code.

Compiling the Code

The default in Visual Basic is to pass arguments by value. However, it is good programming practice to include either the ByVal or ByRef keyword with every declared parameter. This makes your code easier to read.

See also

How to: Force an Argument to Be Passed by Value

The procedure declaration determines the passing mechanism. If a parameter is declared ByRef, Visual Basic expects to pass the corresponding argument by reference. This allows the procedure to change the value of the programming element underlying the argument in the calling code. If you wish to protect the underlying element against such change, you can override the ByRef passing mechanism in the procedure call by enclosing the argument name in parentheses. These parentheses are in addition to the parentheses enclosing the argument list in the call.

The calling code cannot override a ByVal mechanism.

To force an argument to be passed by value

  • If the corresponding parameter is declared ByVal in the procedure, you do not need to take any additional steps. Visual Basic already expects to pass the argument by value.

  • If the corresponding parameter is declared ByRef in the procedure, enclose the argument in parentheses in the procedure call.

Example

The following example overrides a ByRef parameter declaration. In the call that forces ByVal, note the two levels of parentheses.

VB
Sub setNewString(ByRef inString As String)
    inString = "This is a new value for the inString argument."
    MsgBox(inString)
End Sub
VB
Dim str As String = "Cannot be replaced if passed ByVal"

' The following call passes str ByVal even though it is declared ByRef.
Call setNewString((str))
' The parentheses around str protect it from change.
MsgBox(str)

' The following call allows str to be passed ByRef as declared.
Call setNewString(str)
' Variable str is not protected from change.
MsgBox(str)

When str is enclosed in extra parentheses within the argument list, the setNewString procedure cannot change its value in the calling code, and MsgBox displays "Cannot be replaced if passed ByVal". When str is not enclosed in extra parentheses, the procedure can change it, and MsgBox displays "This is a new value for the inString argument."

Compiling the Code

When you pass a variable by reference, you must use the ByRef keyword to specify this mechanism.

The default in Visual Basic is to pass arguments by value. However, it is good programming practice to include either the ByVal or ByRef keyword with every declared parameter. This makes your code easier to read.

Robust Programming

If a procedure declares a parameter ByRef, the correct execution of the code might depend on being able to change the underlying element in the calling code. If the calling code overrides this calling mechanism by enclosing the argument in parentheses, or if it passes a nonmodifiable argument, the procedure cannot change the underlying element. This might produce unexpected results in the calling code.

.NET Framework Security

There is always a potential risk in allowing a procedure to change the value underlying an argument in the calling code. Make sure you expect this value to be changed, and be prepared to check it for validity before using it.

See also

Passing Arguments by Position and by Name

When you call a Sub or Function procedure, you can pass arguments by position — in the order in which they appear in the procedure's definition — or you can pass them by name, without regard to position.

When you pass an argument by name, you specify the argument's declared name followed by a colon and an equal sign (:=), followed by the argument value. You can supply named arguments in any order.

For example, the following Sub procedure takes three arguments:

VB
Public Class StudentInfo
    Shared Sub Display(ByVal name As String,
                Optional ByVal age As Short = 0,
                Optional ByVal birth As Date = #1/1/2000#)

        Console.WriteLine($"Name = {name}; age = {age}; birth date = {birth:d}")
    End Sub
End Class

When you call this procedure, you can supply the arguments by position, by name, or by using a mixture of both.

Passing Arguments by Position

You can call the Display method with its arguments passed by position and delimited by commas, as shown in the following example:

VB
StudentInfo.Display("Mary", 19, #9/21/1998#)

If you omit an optional argument in a positional argument list, you must hold its place with a comma. The following example calls the Display method without the age argument:

VB
StudentInfo.Display("Mary",, #9/21/1998#)

Passing Arguments by Name

Alternatively, you can call Display with the arguments passed by name, also delimited by commas, as shown in the following example:

VB
StudentInfo.Display(age:=19, birth:=#9/21/1998#, name:="Mary")

Passing arguments by name in this way is especially useful when you call a procedure that has more than one optional argument. If you supply arguments by name, you do not have to use consecutive commas to denote missing positional arguments. Passing arguments by name also makes it easier to keep track of which arguments you are passing and which ones you are omitting.

Mixing Arguments by Position and by Name

You can supply arguments both by position and by name in a single procedure call, as shown in the following example:

VB
StudentInfo.Display("Mary", birth:=#9/21/1998#)

In the preceding example, no extra comma is necessary to hold the place of the omitted age argument, since birth is passed by name.

In versions of Visual Basic before 15.5, when you supply arguments by a mixture of position and name, the positional arguments must all come first. Once you supply an argument by name, any remaining arguments must all be passed by name. For example, the following call to the Display method displays compiler error BC30241: Named argument expected.

VB
StudentInfo.Display("Mary", age:=19, #9/21/1998#)

Starting with Visual Basic 15.5, positional arguments can follow named arguments if the ending positional arguments are in the correct position. If compiled under Visual Basic 15.5, the previous call to the Display method compiles successfully and no longer generates compiler error BC30241.

This ability to mix and match named and positional arguments in any order is particularly useful when you want to use a named argument to make your code more readable. For example, the following Person class constructor requires two arguments of type Person, both of which can be Nothing.

VB
Public Sub New(name As String, father As Person, mother As Person, dateOfBirth As Date)

Using mixed named and positional arguments helps to make the intent of the code clear when the value of the father and mother arguments is Nothing:

VB
Dim p = New Person("Mary", father:=Nothing, mother:=Nothing, #9/21/1998#)

To follow positional arguments with named arguments, you must add the following element to your Visual Basic project (*.vbproj) file:

XML
<PropertyGroup>
  <LangVersion>15.5</LangVersion>
</PropertyGroup>

For more information see setting the Visual Basic language version.

Restrictions on Supplying Arguments by Name

You cannot pass arguments by name to avoid entering required arguments. You can omit only the optional arguments.

You cannot pass a parameter array by name. This is because when you call the procedure, you supply an indefinite number of comma-separated arguments for the parameter array, and the compiler cannot associate more than one argument with a single name.

See also

Optional Parameters

You can specify that a procedure parameter is optional and no argument has to be supplied for it when the procedure is called. Optional parameters are indicated by the Optional keyword in the procedure definition. The following rules apply:

  • Every optional parameter in the procedure definition must specify a default value.

  • The default value for an optional parameter must be a constant expression.

  • Every parameter following an optional parameter in the procedure definition must also be optional.

The following syntax shows a procedure declaration with an optional parameter:

VB
Sub name(ByVal parameter1 As datatype1, Optional ByVal parameter2 As datatype2 = defaultvalue)  

Calling Procedures with Optional Parameters

When you call a procedure with an optional parameter, you can choose whether to supply the argument. If you do not, the procedure uses the default value declared for that parameter.

When you omit one or more optional arguments in the argument list, you use successive commas to mark their positions. The following example call supplies the first and fourth arguments but not the second or third:

VB
Sub name(argument 1, , , argument 4)  

The following example makes several calls to the MsgBox function. MsgBox has one required parameter and two optional parameters.

The first call to MsgBox supplies all three arguments in the order that MsgBox defines them. The second call supplies only the required argument. The third and fourth calls supply the first and third arguments. The third call does this by position, and the fourth call does it by name.

VB
MsgBox("Important message", MsgBoxStyle.Critical, "MsgBox Example")
MsgBox("Just display this message.")
MsgBox("Test message", , "Title bar text")
MsgBox(Title:="Title bar text", Prompt:="Test message")

Determining Whether an Optional Argument Is Present

A procedure cannot detect at run time whether a given argument has been omitted or the calling code has explicitly supplied the default value. If you need to make this distinction, you can set an unlikely value as the default. The following procedure defines the optional parameter office, and tests for its default value, QJZ, to see if it has been omitted in the call:

VB
Sub notify(ByVal company As String, Optional ByVal office As String = "QJZ")
    If office = "QJZ" Then
        Debug.WriteLine("office not supplied -- using Headquarters")
        office = "Headquarters"
    End If
    ' Insert code to notify headquarters or specified office.
End Sub

If the optional parameter is a reference type such as a String, you can use Nothing as the default value, provided this is not an expected value for the argument.

Optional Parameters and Overloading

Another way to define a procedure with optional parameters is to use overloading. If you have one optional parameter, you can define two overloaded versions of the procedure, one accepting the parameter and one without it. This approach becomes more complicated as the number of optional parameters increases. However, its advantage is that you can be absolutely sure whether the calling program supplied each optional argument.

See also

Parameter Arrays

Usually, you cannot call a procedure with more arguments than the procedure declaration specifies. When you need an indefinite number of arguments, you can declare a parameter array, which allows a procedure to accept an array of values for a parameter. You do not have to know the number of elements in the parameter array when you define the procedure. The array size is determined individually by each call to the procedure.

Declaring a ParamArray

You use the ParamArray keyword to denote a parameter array in the parameter list. The following rules apply:

  • A procedure can define only one parameter array, and it must be the last parameter in the procedure definition.

  • The parameter array must be passed by value. It is good programming practice to explicitly include the ByVal keyword in the procedure definition.

  • The parameter array is automatically optional. Its default value is an empty one-dimensional array of the parameter array's element type.

  • All parameters preceding the parameter array must be required. The parameter array must be the only optional parameter.

Calling a ParamArray

When you call a procedure that defines a parameter array, you can supply the argument in any one of the following ways:

  • Nothing — that is, you can omit the ParamArray argument. In this case, an empty array is passed to the procedure. You can also pass the Nothing keyword, with the same effect.

  • A list of an arbitrary number of arguments, separated by commas. The data type of each argument must be implicitly convertible to the ParamArray element type.

  • An array with the same element type as the parameter array's element type.

In all cases, the code within the procedure treats the parameter array as a one-dimensional array with elements of the same data type as the ParamArray data type.

Important

Whenever you deal with an array which can be indefinitely large, there is a risk of overrunning some internal capacity of your application. If you accept a parameter array, you should test for the size of the array that the calling code passed to it. Take appropriate steps if it is too large for your application. For more information, see Arrays.

Example

The following example defines and calls the function calcSum. The ParamArray modifier for the parameter args enables the function to accept a variable number of arguments.

VB
Module Module1

    Sub Main()
        ' In the following function call, CalcSum's local variables 
        ' are assigned the following values: args(0) = 4, args(1) = 3, 
        ' and so on. The displayed sum is 10.
        Dim returnedValue As Double = CalcSum(4, 3, 2, 1)
        Console.WriteLine("Sum: " & returnedValue)
        ' Parameter args accepts zero or more arguments. The sum 
        ' displayed by the following statements is 0.
        returnedValue = CalcSum()
        Console.WriteLine("Sum: " & returnedValue)
    End Sub

    Public Function CalcSum(ByVal ParamArray args() As Double) As Double
        CalcSum = 0
        If args.Length <= 0 Then Exit Function
        For i As Integer = 0 To UBound(args, 1)
            CalcSum += args(i)
        Next i
    End Function

End Module

The following example defines a procedure with a parameter array, and outputs the values of all the array elements passed to the parameter array.

VB
Sub studentScores(ByVal name As String, ByVal ParamArray scores() As String)
    Debug.WriteLine("Scores for " & name & ":" & vbCrLf)
    ' Use UBound to determine largest subscript of the array.
    For i As Integer = 0 To UBound(scores, 1)
        Debug.WriteLine("Score " & i & ": " & scores(i))
    Next i
End Sub
VB
Call studentScores("Anne", "10", "26", "32", "15", "22", "24", "16")
Call studentScores("Mary", "High", "Low", "Average", "High")
Dim JohnScores() As String = {"35", "Absent", "21", "30"}
Call studentScores("John", JohnScores)

See also

Support for reference return values

Starting with C# 7.0, the C# language supports reference return values. One way to understand reference return values is that they are the opposite of arguments that are passed by reference to a method. When an argument passed by reference is modified, the changes are reflected in value of the variable on the caller. When an method provides a reference return value to a caller, modifications made to the reference return value by the caller are reflected in the called method's data.

Visual Basic does not allow you to author methods with reference return values, but it does allow you to consume reference return values. In other words, you can call a method with a reference return value and modify that return value, and changes to the reference return value are reflected in the called method's data.

Modifying the ref return value directly

For methods that always succeed and have no ByRef parameters, you can modify the reference return value directly. You do this by assigning the new value to the expressions that returns the reference return value.

The following C# example defines a NumericValue.IncrementValue method that increments an internal value and returns it as a reference return value.

C#
using System;

public class NumericValue
{
   private int value = 0;
       
   public NumericValue(int value)
   {
      this.value = value;
   }     
   
   public ref int IncrementValue()
   {
      value++;
      return ref value;
   }
   
   public int GetValue()
   {
      return value;
   }
}

The reference return value is then modified by the caller in the following Visual Basic example. Note that the line with the NumericValue.IncrementValue method call does not assign a value to the method. Instead, it assigns a value to the reference return value returned by the method.

VB
Module Example
   Public Sub Main()
      Dim n As New NumericValue(15)
      n.IncrementValue() += 12
      Console.WriteLine(n.GetValue) 
   End Sub
End Module
' Output:   28

Using a helper method

In other cases, modifying the reference return value of a method call directly may not always be desirable. For example, a search method that returns a string may not always find a match. In that case, you want to modify the reference return value only if the search is successful.

The following C# example illustrates this scenario. It defines a Sentence class written in C# includes a FindNext method that finds the next word in a sentence that begins with a specified substring. The string is returned as a reference return value, and a Boolean variable passed by reference to the method indicates whether the search was successful. The reference return value indicates that the caller can not only read the returned value; he or she can also modify it, and that modification is reflected in the data contained internally in the Sentence class.

C#
using System;
 
public class Sentence
{
    private string[] words;
    private int currentSearchPointer;
    
    public Sentence(string sentence)
    {
        words = sentence.Split(' ');
        currentSearchPointer = -1;
    }
    
    public ref string FindNext(string startWithString, ref bool found)
    {
        for (int count = currentSearchPointer + 1; count < words.Length; count++)
        {
            if (words[count].StartsWith(startWithString))
            {
                currentSearchPointer = count;
                found = true;
                return ref words[currentSearchPointer];
            }
        }
        currentSearchPointer = -1;
        found = false;
        return ref words[0];
    }
    
    public string GetSentence()
    {
        string stringToReturn = null;
        foreach (var word in words)
            stringToReturn += $"{word} ";
      
        return stringToReturn.Trim();    
    }
}

Directly modifying the reference return value in this case is not reliable, since the method call may fail to find a match and return the first word in the sentence. In that case, the caller will inadvertently modify the first word of the sentence. This could be prevented by the caller returning a null (or Nothing in Visual Basic). But in that case, attempting to modify a string whose value is Nothing throws a NullReferenceException. If could also be prevented by the caller returning String.Empty, but this requires that the caller define a string variable whose value is String.Empty. While the caller can modify that string, the modification itself serves no purpose, since the modified string has no relationship to the words in the sentence stored by the Sentence class.

The best way to handle this scenario is to pass the reference return value by reference to a helper method. The helper method then contains the logic to determine whether the method call succeeded and, if it did, to modify the reference return value. The following example provides a possible implementation.

VB
Module Example
   Public Sub Main()
      Dim sentence As New Sentence("A time to see the world is now.")
      Dim found = False
      Dim returns = RefHelper(sentence.FindNext("A", found), "A good", found) 
      Console.WriteLine(sentence.GetSentence()) 
   End Sub
   
   Private Function RefHelper(ByRef stringFound As String, replacement As String, success As Boolean) _ 
                    As (originalString As String, found As Boolean) 
      Dim originalString = stringFound
      If found Then stringFound = replacement
      Return (originalString, found)   
   End Function
End Module
' The example displays the following output:
'      A good time to see the world is now.

See also

Recursive Procedures

A recursive procedure is one that calls itself. In general, this is not the most effective way to write Visual Basic code.

The following procedure uses recursion to calculate the factorial of its original argument.

VB
Function factorial(ByVal n As Integer) As Integer
    If n <= 1 Then
        Return 1
    Else
        Return factorial(n - 1) * n
    End If
End Function

Considerations with Recursive Procedures

Limiting Conditions. You must design a recursive procedure to test for at least one condition that can terminate the recursion, and you must also handle the case where no such condition is satisfied within a reasonable number of recursive calls. Without at least one condition that can be met without fail, your procedure runs a high risk of executing in an infinite loop.

Memory Usage. Your application has a limited amount of space for local variables. Each time a procedure calls itself, it uses more of that space for additional copies of its local variables. If this process continues indefinitely, it eventually causes a StackOverflowException error.

Efficiency. You can almost always substitute a loop for recursion. A loop does not have the overhead of passing arguments, initializing additional storage, and returning values. Your performance can be much better without recursive calls.

Mutual Recursion. You might observe very poor performance, or even an infinite loop, if two procedures call each other. Such a design presents the same problems as a single recursive procedure, but can be harder to detect and debug.

Calling with Parentheses. When a Function procedure calls itself recursively, you must follow the procedure name with parentheses, even if there is no argument list. Otherwise, the function name is taken as representing the return value of the function.

Testing. If you write a recursive procedure, you should test it very carefully to make sure it always meets some limiting condition. You should also ensure that you cannot run out of memory due to having too many recursive calls.

See also

Procedure Overloading

Overloading a procedure means defining it in multiple versions, using the same name but different parameter lists. The purpose of overloading is to define several closely related versions of a procedure without having to differentiate them by name. You do this by varying the parameter list.

Overloading Rules

When you overload a procedure, the following rules apply:

  • Same Name. Each overloaded version must use the same procedure name.

  • Different Signature. Each overloaded version must differ from all other overloaded versions in at least one of the following respects:

    • Number of parameters

    • Order of the parameters

    • Data types of the parameters

    • Number of type parameters (for a generic procedure)

    • Return type (only for a conversion operator)

    Together with the procedure name, the preceding items are collectively called the signature of the procedure. When you call an overloaded procedure, the compiler uses the signature to check that the call correctly matches the definition.

  • Items Not Part of Signature. You cannot overload a procedure without varying the signature. In particular, you cannot overload a procedure by varying only one or more of the following items:

    • Procedure modifier keywords, such as Public, Shared, and Static

    • Parameter or type parameter names

    • Type parameter constraints (for a generic procedure)

    • Parameter modifier keywords, such as ByRef and Optional

    • Whether it returns a value

    • The data type of the return value (except for a conversion operator)

    The items in the preceding list are not part of the signature. Although you cannot use them to differentiate between overloaded versions, you can vary them among overloaded versions that are properly differentiated by their signatures.

  • Late-Bound Arguments. If you intend to pass a late bound object variable to an overloaded version, you must declare the appropriate parameter as Object.

Multiple Versions of a Procedure

Suppose you are writing a Sub procedure to post a transaction against a customer's balance, and you want to be able to refer to the customer either by name or by account number. To accommodate this, you can define two different Sub procedures, as in the following example:

VB
Sub postName(ByVal custName As String, ByVal amount As Single)
    ' Insert code to access customer record by customer name.
End Sub
Sub postAcct(ByVal custAcct As Integer, ByVal amount As Single)
    ' Insert code to access customer record by account number.
End Sub

Overloaded Versions

An alternative is to overload a single procedure name. You can use the Overloads keyword to define a version of the procedure for each parameter list, as follows:

VB
Overloads Sub post(ByVal custName As String, ByVal amount As Single)
    ' Insert code to access customer record by customer name.
End Sub
Overloads Sub post(ByVal custAcct As Integer, ByVal amount As Single)
    ' Insert code to access customer record by account number.
End Sub

Additional Overloads

If you also wanted to accept a transaction amount in either Decimal or Single, you could further overload post to allow for this variation. If you did this to each of the overloads in the preceding example, you would have four Sub procedures, all with the same name but with four different signatures.

Advantages of Overloading

The advantage of overloading a procedure is in the flexibility of the call. To use the post procedure declared in the preceding example, the calling code can obtain the customer identification as either a String or an Integer, and then call the same procedure in either case. The following example illustrates this:

VB
Imports MSVB = Microsoft.VisualBasic
VB
Dim customer As String
Dim accountNum As Integer
Dim amount As Single
customer = MSVB.Interaction.InputBox("Enter customer name or number")
amount = MSVB.Interaction.InputBox("Enter transaction amount")
Try
    accountNum = CInt(customer)
    Call post(accountNum, amount)
Catch
    Call post(customer, amount)
End Try

See also

How to: Define Multiple Versions of a Procedure

You can define a procedure in multiple versions by overloading it, using the same name but a different parameter list for each version. The purpose of overloading is to define several closely related versions of a procedure without having to differentiate them by name.

For more information, see Procedure Overloading.

To define multiple versions of a procedure

  1. Write a Sub or Function declaration statement for each version of the procedure you want to define. Use the same procedure name in every declaration.

  2. Precede the Sub or Function keyword in each declaration with the Overloads keyword. You can optionally omit Overloads in the declarations, but if you include it in any of the declarations, you must include it in every declaration.

  3. Following each declaration statement, write procedure code to handle the specific case where the calling code supplies arguments matching that version's parameter list. You do not have to test for which parameters the calling code has supplied. Visual Basic passes control to the matching version of your procedure.

  4. Terminate each version of the procedure with the End Sub or End Function statement as appropriate.

Example

The following example defines a Sub procedure to post a transaction against a customer's balance. It uses the Overloads keyword to define two versions of the procedure, one that accepts the customer by name and the other by account number.

VB
Overloads Sub post(ByVal custName As String, ByVal amount As Single)
    ' Insert code to access customer record by customer name.
End Sub
Overloads Sub post(ByVal custAcct As Integer, ByVal amount As Single)
    ' Insert code to access customer record by account number.
End Sub

The calling code can obtain the customer identification as either a String or an Integer, and then use the same calling statement in either case.

For information on how to call these versions of the post procedure, see How to: Call an Overloaded Procedure.

Compiling the Code

Make sure each of your overloaded versions has the same procedure name but a different parameter list.

See also

How to: Call an Overloaded Procedure

The advantage of overloading a procedure is in the flexibility of the call. The calling code can obtain the information it needs to pass to the procedure and then call a single procedure name, no matter what arguments it is passing.

To call a procedure that has more than one version defined

  1. In the calling code, determine which data to pass to the procedure.

  2. Write the procedure call in the normal way, presenting the data in the argument list. Be sure the arguments match the parameter list in one of the versions defined for the procedure.

  3. You do not have to determine which version of the procedure to call. Visual Basic passes control to the version matching your argument list.

    The following example calls the post procedure declared in How to: Define Multiple Versions of a Procedure. It obtains the customer identification, determines whether it is a String or an Integer, and then in either case calls the same procedure.

    VB
    Imports MSVB = Microsoft.VisualBasic
    
    VB
    Dim customer As String
    Dim accountNum As Integer
    Dim amount As Single
    customer = MSVB.Interaction.InputBox("Enter customer name or number")
    amount = MSVB.Interaction.InputBox("Enter transaction amount")
    Try
        accountNum = CInt(customer)
        Call post(accountNum, amount)
    Catch
        Call post(customer, amount)
    End Try
    

See also

How to: Overload a Procedure that Takes Optional Parameters

If a procedure has one or more Optional parameters, you cannot define an overloaded version matching any of its implicit overloads. For more information, see "Implicit Overloads for Optional Parameters" in Considerations in Overloading Procedures.

One Optional Parameter

To overload a procedure that takes one optional parameter

  1. Write a Sub or Function declaration statement that includes the optional parameter in the parameter list. Do not use the Optional keyword in this overloaded version.

  2. Precede the Sub or Function keyword with the Overloads keyword.

  3. Write the procedure code that should execute when the calling code supplies the optional argument.

  4. Terminate the procedure with the End Sub or End Function statement as appropriate.

  5. Write a second declaration statement that is identical to the first declaration except that it does not include the optional parameter in the parameter list.

  6. Write the procedure code that should execute when the calling code does not supply the optional argument. Terminate the procedure with the End Sub or End Function statement as appropriate.

    The following example shows a procedure defined with an optional parameter, an equivalent set of two overloaded procedures, and finally examples of both invalid and valid overloaded versions.

    VB
Sub q(ByVal b As Byte, Optional ByVal j As Long = 6)
VB
' The preceding definition is equivalent to the following two overloads.
' Overloads Sub q(ByVal b As Byte)
' Overloads Sub q(ByVal b As Byte, ByVal j As Long)
VB
  1. ' Therefore, the following overload is not valid because the signature is already in use.
    ' Overloads Sub q(ByVal c As Byte, ByVal k As Long)
    ' The following overload uses a different signature and is valid.
    Overloads Sub q(ByVal b As Byte, ByVal j As Long, ByVal s As Single)
    

Multiple Optional Parameters

For a procedure with more than one optional parameter, you normally need more than two overloaded versions. For example, if there are two optional parameters, and the calling code can supply or omit each one independently of the other, you need four overloaded versions, one for each possible combination of supplied arguments.

As the number of optional parameters increases, the complexity of the overloading increases. Unless some combinations of supplied arguments are not acceptable, for N optional parameters you need 2 ^ N overloaded versions. Depending on the nature of the procedure, you might find that the clarity of logic justifies the extra effort of defining all the overloaded versions.

To overload a procedure that takes more than one optional parameter

  1. Determine which combinations of supplied optional arguments are acceptable to the logic of the procedure. An unacceptable combination might arise if one optional parameter depends on another. For example, if one parameter accepts a person's name and another accepts the person's age, a combination of arguments supplying the age but omitting the name is unacceptable.

  2. For each acceptable combination of supplied optional arguments, write a Sub or Function declaration statement that defines the corresponding parameter list. Do not use the Optional keyword.

  3. In each declaration, precede the Sub or Function keyword with the Overloads keyword.

  4. Following each declaration, write the procedure code that should execute when the calling code supplies an argument list corresponding to that declaration's parameter list.

  5. Terminate each procedure with the End Sub or End Function statement as appropriate.

See also

How to: Overload a Procedure that Takes an Indefinite Number of Parameters

If a procedure has a ParamArray parameter, you cannot define an overloaded version taking a one-dimensional array for the parameter array. For more information, see "Implicit Overloads for a ParamArray Parameter" in Considerations in Overloading Procedures.

To overload a procedure that takes a variable number of parameters

  1. Ascertain that the procedure and calling code logic benefits from overloaded versions more than from a ParamArray parameter. See "Overloads and ParamArrays" in Considerations in Overloading Procedures.

  2. Determine which numbers of supplied values the procedure should accept in the variable part of the parameter list. This might include the case of no value, and it might include the case of a single one-dimensional array.

  3. For each acceptable number of supplied values, write a Sub or Function declaration statement that defines the corresponding parameter list. Do not use either the Optional or the ParamArray keyword in this overloaded version.

  4. In each declaration, precede the Sub or Function keyword with the Overloads keyword.

  5. Following each declaration, write the procedure code that should execute when the calling code supplies values corresponding to that declaration's parameter list.

  6. Terminate each procedure with the End Sub or End Function statement as appropriate.

Example

The following example shows a procedure defined with a ParamArray parameter, and then an equivalent set of overloaded procedures.

VB
Sub p(ByVal d As Date, ByVal ParamArray c() As Char)
VB
' The preceding definition is equivalent to the following overloads.
' Overloads Sub p(ByVal d As Date)
' Overloads Sub p(ByVal d As Date, ByVal c() As Char)
' Overloads Sub p(ByVal d As Date, ByVal c1 As Char)
' Overloads Sub p(ByVal d As Date, ByVal c1 As Char, ByVal c2 As Char)
' And so on, with an additional Char argument in each successive overload.

You cannot overload such a procedure with a parameter list that takes a one-dimensional array for the parameter array. However, you can use the signatures of the other implicit overloads. The following declarations illustrate this.

VB
' The following overload is not valid because it takes an array for the parameter array.
' Overloads Sub p(ByVal x As Date, ByVal y() As Char)
' The following overload takes a single value for the parameter array and is valid.
Overloads Sub p(ByVal z As Date, ByVal w As Char)

The code in the overloaded versions does not have to test whether the calling code supplied one or more values for the ParamArray parameter, or if so, how many. Visual Basic passes control to the version matching the calling argument list.

Compiling the Code

Because a procedure with a ParamArray parameter is equivalent to a set of overloaded versions, you cannot overload such a procedure with a parameter list corresponding to any of these implicit overloads. For more information, see Considerations in Overloading Procedures.

.NET Framework Security

Whenever you deal with an array which can be indefinitely large, there is a risk of overrunning some internal capacity of your application. If you accept a parameter array, you should test for the length of the array the calling code passed to it, and take appropriate steps if it is too large for your application.

See also

Considerations in Overloading Procedures

When you overload a procedure, you must use a different signature for each overloaded version. This usually means each version must specify a different parameter list. For more information, see "Different Signature" in Procedure Overloading.

You can overload a Function procedure with a Sub procedure, and vice versa, provided they have different signatures. Two overloads cannot differ only in that one has a return value and the other does not.

You can overload a property the same way you overload a procedure, and with the same restrictions. However, you cannot overload a procedure with a property, or vice versa.

Alternatives to Overloaded Versions

You sometimes have alternatives to overloaded versions, particularly when the presence of arguments is optional or their number is variable.

Keep in mind that optional arguments are not necessarily supported by all languages, and parameter arrays are limited to Visual Basic. If you are writing a procedure that is likely to be called from code written in any of several different languages, overloaded versions offer the greatest flexibility.

Overloads and Optional Arguments

When the calling code can optionally supply or omit one or more arguments, you can define multiple overloaded versions or use optional parameters.

When to Use Overloaded Versions

You can consider defining a series of overloaded versions in the following cases:

  • The logic in the procedure code is significantly different depending on whether the calling code supplies an optional argument or not.

  • The procedure code cannot reliably test whether the calling code has supplied an optional argument. This is the case, for example, if there is no possible candidate for a default value that the calling code could not be expected to supply.

When to Use Optional Parameters

You might prefer one or more optional parameters in the following cases:

  • The only required action when the calling code does not supply an optional argument is to set the parameter to a default value. In such a case, the procedure code can be less complicated if you define a single version with one or more Optional parameters.

For more information, see Optional Parameters.

Overloads and ParamArrays

When the calling code can pass a variable number of arguments, you can define multiple overloaded versions or use a parameter array.

When to Use Overloaded Versions

You can consider defining a series of overloaded versions in the following cases:

  • You know that the calling code never passes more than a small number of values to the parameter array.

  • The logic in the procedure code is significantly different depending on how many values the calling code passes.

  • The calling code can pass values of different data types.

When to Use a Parameter Array

You are better served by a ParamArray parameter in the following cases:

  • You are not able to predict how many values the calling code can pass to the parameter array, and it could be a large number.

  • The procedure logic lends itself to iterating through all the values the calling code passes, performing essentially the same operations on every value.

For more information, see Parameter Arrays.

Implicit Overloads for Optional Parameters

A procedure with an Optional parameter is equivalent to two overloaded procedures, one with the optional parameter and one without it. You cannot overload such a procedure with a parameter list corresponding to either of these. The following declarations illustrate this.

VB
Overloads Sub q(ByVal b As Byte, Optional ByVal j As Long = 6)
VB
' The preceding definition is equivalent to the following two overloads.
' Overloads Sub q(ByVal b As Byte)
' Overloads Sub q(ByVal b As Byte, ByVal j As Long)
VB
' Therefore, the following overload is not valid because the signature is already in use.
' Overloads Sub q(ByVal c As Byte, ByVal k As Long)
' The following overload uses a different signature and is valid.
Overloads Sub q(ByVal b As Byte, ByVal j As Long, ByVal s As Single)

For a procedure with more than one optional parameter, there is a set of implicit overloads, arrived at by logic similar to that in the preceding example.

Implicit Overloads for a ParamArray Parameter

The compiler considers a procedure with a ParamArray parameter to have an infinite number of overloads, differing from each other in what the calling code passes to the parameter array, as follows:

  • One overload for when the calling code does not supply an argument to the ParamArray

  • One overload for when the calling code supplies a one-dimensional array of the ParamArray element type

  • For every positive integer, one overload for when the calling code supplies that number of arguments, each of the ParamArray element type

The following declarations illustrate these implicit overloads.

VB
Overloads Sub p(ByVal d As Date, ByVal ParamArray c() As Char)
VB
' The preceding definition is equivalent to the following overloads.
' Overloads Sub p(ByVal d As Date)
' Overloads Sub p(ByVal d As Date, ByVal c() As Char)
' Overloads Sub p(ByVal d As Date, ByVal c1 As Char)
' Overloads Sub p(ByVal d As Date, ByVal c1 As Char, ByVal c2 As Char)
' And so on, with an additional Char argument in each successive overload.

You cannot overload such a procedure with a parameter list that takes a one-dimensional array for the parameter array. However, you can use the signatures of the other implicit overloads. The following declarations illustrate this.

VB
' The following overload is not valid because it takes an array for the parameter array.
' Overloads Sub p(ByVal x As Date, ByVal y() As Char)
' The following overload takes a single value for the parameter array and is valid.
Overloads Sub p(ByVal z As Date, ByVal w As Char)

Typeless Programming as an Alternative to Overloading

If you want to allow the calling code to pass different data types to a parameter, an alternative approach is typeless programming. You can set the type checking switch to Off with either the Option Strict Statement or the /optionstrict compiler option. Then you do not have to declare the parameter's data type. However, this approach has the following disadvantages compared to overloading:

  • Typeless programming produces less efficient execution code.

  • The procedure must test for every data type it anticipates being passed.

  • The compiler cannot signal an error if the calling code passes a data type that the procedure does not support.

See also

Overload Resolution

When the Visual Basic compiler encounters a call to a procedure that is defined in several overloaded versions, the compiler must decide which of the overloads to call. It does this by performing the following steps:

  1. Accessibility. It eliminates any overload with an access level that prevents the calling code from calling it.

  2. Number of Parameters. It eliminates any overload that defines a different number of parameters than are supplied in the call.

  3. Parameter Data Types. The compiler gives instance methods preference over extension methods. If any instance method is found that requires only widening conversions to match the procedure call, all extension methods are dropped and the compiler continues with only the instance method candidates. If no such instance method is found, it continues with both instance and extension methods.

    In this step, it eliminates any overload for which the data types of the calling arguments cannot be converted to the parameter types defined in the overload.

  4. Narrowing Conversions. It eliminates any overload that requires a narrowing conversion from the calling argument types to the defined parameter types. This is true whether the type checking switch (Option Strict Statement) is On or Off.

  5. Least Widening. The compiler considers the remaining overloads in pairs. For each pair, it compares the data types of the defined parameters. If the types in one of the overloads all widen to the corresponding types in the other, the compiler eliminates the latter. That is, it retains the overload that requires the least amount of widening.

  6. Single Candidate. It continues considering overloads in pairs until only one overload remains, and it resolves the call to that overload. If the compiler cannot reduce the overloads to a single candidate, it generates an error.

The following illustration shows the process that determines which of a set of overloaded versions to call.

Flow diagram of overload resolution process

The following example illustrates this overload resolution process.

VB
Overloads Sub z(ByVal x As Byte, ByVal y As Double)
End Sub
Overloads Sub z(ByVal x As Short, ByVal y As Single)
End Sub
Overloads Sub z(ByVal x As Integer, ByVal y As Single)
End Sub
VB
Dim r, s As Short
Call z(r, s)
Dim p As Byte, q As Short
' The following statement causes an overload resolution error.
Call z(p, q)

In the first call, the compiler eliminates the first overload because the type of the first argument (Short) narrows to the type of the corresponding parameter (Byte). It then eliminates the third overload because each argument type in the second overload (Short and Single) widens to the corresponding type in the third overload (Integer and Single). The second overload requires less widening, so the compiler uses it for the call.

In the second call, the compiler cannot eliminate any of the overloads on the basis of narrowing. It eliminates the third overload for the same reason as in the first call, because it can call the second overload with less widening of the argument types. However, the compiler cannot resolve between the first and second overloads. Each has one defined parameter type that widens to the corresponding type in the other (Byte to Short, but Single to Double). The compiler therefore generates an overload resolution error.

Overloaded Optional and ParamArray Arguments

If two overloads of a procedure have identical signatures except that the last parameter is declared Optional in one and ParamArray in the other, the compiler resolves a call to that procedure as follows:

If the call supplies the last argument as The compiler resolves the call to the overload declaring the last argument as
No value (argument omitted) Optional
A single value Optional
Two or more values in a comma-separated list ParamArray
An array of any length (including an empty array) ParamArray

See also

Troubleshooting Procedures

This page lists some common problems that can occur when working with procedures.

Returning an Array Type from a Function Procedure

If a Function procedure returns an array data type, you cannot use the Function name to store values in the elements of the array. If you attempt to do this, the compiler interprets it as a call to the Function. The following example generates compiler errors.

Function allOnes(ByVal n As Integer) As Integer()

For i As Integer = 1 To n - 1

' The following statement generates a COMPILER ERROR .

allOnes(i) = 1

Next i

' The following statement generates a COMPILER ERROR .

Return allOnes()

End Function

The statement allOnes(i) = 1 generates a compiler error because it appears to call allOnes with an argument of the wrong data type (a singleton Integer instead of an Integer array). The statement Return allOnes() generates a compiler error because it appears to call allOnes with no argument.

Correct Approach: To be able to modify the elements of an array that is to be returned, define an internal array as a local variable. The following example compiles without error.

VB
Function allOnes(ByVal n As Integer) As Integer()
    Dim i As Integer, iArray(n) As Integer
    For i = 0 To n - 1
        iArray(i) = 1
    Next i
    Return iArray
End Function

Argument Not Being Modified by Procedure Call

If you intend to allow a procedure to change a programming element underlying an argument in the calling code, you must pass it by reference. But a procedure can access the elements of a reference type argument even if you pass it by value.

  • Underlying Variable. To allow the procedure to replace the value of the underlying variable element itself, the procedure must declare the parameter ByRef. Also, the calling code must not enclose the argument in parentheses, because that would override the ByRef passing mechanism.

  • Reference Type Elements. If you declare a parameter ByVal, the procedure cannot modify the underlying variable element itself. However, if the argument is a reference type, the procedure can modify the members of the object to which it points, even though it cannot replace the variable's value. For example, if the argument is an array variable, the procedure cannot assign a new array to it, but it can change one or more of its elements. The changed elements are reflected in the underlying array variable in the calling code.

The following example defines two procedures that take an array variable by value and operate on its elements. Procedure increase simply adds one to each element. Procedure replace assigns a new array to the parameter a() and then adds one to each element. However, the reassignment does not affect the underlying array variable in the calling code because a() is declared ByVal.

VB
Public Sub increase(ByVal a() As Long)
    For j As Integer = 0 To UBound(a)
        a(j) = a(j) + 1
    Next j
End Sub
VB
Public Sub replace(ByVal a() As Long)
    Dim k() As Long = {100, 200, 300}
    a = k
    For j As Integer = 0 To UBound(a)
        a(j) = a(j) + 1
    Next j
End Sub

The following example makes calls to increase and replace.

VB
Dim n() As Long = {10, 20, 30, 40}
Call increase(n)
MsgBox("After increase(n): " & CStr(n(0)) & ", " & 
    CStr(n(1)) & ", " & CStr(n(2)) & ", " & CStr(n(3)))
Call replace(n)
MsgBox("After replace(n): " & CStr(n(0)) & ", " & 
    CStr(n(1)) & ", " & CStr(n(2)) & ", " & CStr(n(3)))

The first MsgBox call displays "After increase(n): 11, 21, 31, 41". Because n is a reference type, increase can change its members, even though it is passed ByVal.

The second MsgBox call displays "After replace(n): 11, 21, 31, 41". Because n is passed ByVal, replace cannot modify the variable n by assigning a new array to it. When replace creates the new array instance k and assigns it to the local variable a, it loses the reference to n passed in by the calling code. When it increments the members of a, only the local array k is affected.

Correct Approach: To be able to modify an underlying variable element itself, pass it by reference. The following example shows the change in the declaration of replace that allows it to replace one array with another in the calling code.

VB
Public Sub replace(ByRef a() As Long)

Unable to Define an Overload

If you want to define an overloaded version of a procedure, you must use the same name but a different signature. If the compiler cannot differentiate your declaration from an overload with the same signature, it generates an error.

The signature of a procedure is determined by the procedure name and the parameter list. Each overload must have the same name as all the other overloads but must differ from all of them in at least one of the other components of the signature. For more information, see Procedure Overloading.

The following items, even though they pertain to the parameter list, are not components of a procedure's signature:

  • Procedure modifier keywords, such as Public, Shared, and Static

  • Parameter names

  • Parameter modifier keywords, such as ByRef and Optional

  • The data type of the return value (except for a conversion operator)

You cannot overload a procedure by varying only one or more of the preceding items.

Correct Approach: To be able to define a procedure overload, you must vary the signature. Because you must use the same name, you must vary the number, order, or data types of the parameters. In a generic procedure, you can vary the number of type parameters. In a conversion operator (CType Function), you can vary the return type.

Overload Resolution with Optional and ParamArray Arguments

If you are overloading a procedure with one or more Optional parameters or a ParamArray parameter, you must avoid duplicating any of the implicit overloads. For information, see Considerations in Overloading Procedures.

Calling a Wrong Version of an Overloaded Procedure

If a procedure has several overloaded versions, you should be familiar with all their parameter lists and understand how Visual Basic resolves calls among the overloads. Otherwise you could call an overload other than the intended one.

When you have determined which overload you want to call, be careful to observe the following rules:

  • Supply the correct number of arguments, and in the correct order.

  • Ideally, your arguments should have the exact same data types as the corresponding parameters. In any case, the data type of each argument must widen to that of its corresponding parameter. This is true even with the Option Strict Statement set to Off. If an overload requires any narrowing conversion from your argument list, that overload is not eligible to be called.

  • If you supply arguments that require widening, make their data types as close as possible to the corresponding parameter data types. If two or more overloads accept your argument data types, the compiler resolves your call to the overload that calls for the least amount of widening.

You can reduce the chance of data type mismatches by using the CType Function conversion keyword when preparing your arguments.

Overload Resolution Failure

When you call an overloaded procedure, the compiler attempts to eliminate all but one of the overloads. If it succeeds, it resolves the call to that overload. If it eliminates all the overloads, or if it cannot reduce the eligible overloads to a single candidate, it generates an error.

The following example illustrates the overload resolution process.

VB
Overloads Sub z(ByVal x As Byte, ByVal y As Double)
End Sub
Overloads Sub z(ByVal x As Short, ByVal y As Single)
End Sub
Overloads Sub z(ByVal x As Integer, ByVal y As Single)
End Sub
VB
Dim r, s As Short
Call z(r, s)
Dim p As Byte, q As Short
' The following statement causes an overload resolution error.
Call z(p, q)

In the first call, the compiler eliminates the first overload because the type of the first argument (Short) narrows to the type of the corresponding parameter (Byte). It then eliminates the third overload because each argument type in the second overload (Short and Single) widens to the corresponding type in the third overload (Integer and Single). The second overload requires less widening, so the compiler uses it for the call.

In the second call, the compiler cannot eliminate any of the overloads on the basis of narrowing. It eliminates the third overload for the same reason as in the first call, because it can call the second overload with less widening of the argument types. However, the compiler cannot resolve between the first and second overloads. Each has one defined parameter type that widens to the corresponding type in the other (Byte to Short, but Single to Double). The compiler therefore generates an overload resolution error.

Correct Approach: To be able to call an overloaded procedure without ambiguity, use CType Function to match the argument data types to the parameter types. The following example shows a call to z that forces resolution to the second overload.

VB
Call z(CType(p, Short), CType(q, Single))

Overload Resolution with Optional and ParamArray Arguments

If two overloads of a procedure have identical signatures except that the last parameter is declared Optional in one and ParamArray in the other, the compiler resolves a call to that procedure according to the closest match. For more information, see Overload Resolution.

See also

Extension Methods

Extension methods enable developers to add custom functionality to data types that are already defined without creating a new derived type. Extension methods make it possible to write a method that can be called as if it were an instance method of the existing type.

Remarks

An extension method can be only a Sub procedure or a Function procedure. You cannot define an extension property, field, or event. All extension methods must be marked with the extension attribute <Extension()> from the System.Runtime.CompilerServices namespace.

The first parameter in an extension method definition specifies which data type the method extends. When the method is run, the first parameter is bound to the instance of the data type that invokes the method.

Example

Description

The following example defines a Print extension to the String data type. The method uses Console.WriteLine to display a string. The parameter of the Print method, aString, establishes that the method extends the String class.

VB
Imports System.Runtime.CompilerServices

Module StringExtensions

    <Extension()> 
    Public Sub Print(ByVal aString As String)
        Console.WriteLine(aString)
    End Sub

End Module

Notice that the extension method definition is marked with the extension attribute <Extension()>. Marking the module in which the method is defined is optional, but each extension method must be marked. System.Runtime.CompilerServices must be imported in order to access the extension attribute.

Extension methods can be declared only within modules. Typically, the module in which an extension method is defined is not the same module as the one in which it is called. Instead, the module that contains the extension method is imported, if it needs to be, to bring it into scope. After the module that contains Print is in scope, the method can be called as if it were an ordinary instance method that takes no arguments, such as ToUpper:

VB
Module Class1

    Sub Main()

        Dim example As String = "Hello"
        ' Call to extension method Print.
        example.Print()

        ' Call to instance method ToUpper.
        example.ToUpper()
        example.ToUpper.Print()

    End Sub

End Module

The next example, PrintAndPunctuate, is also an extension to String, this time defined with two parameters. The first parameter, aString, establishes that the extension method extends String. The second parameter, punc, is intended to be a string of punctuation marks that is passed in as an argument when the method is called. The method displays the string followed by the punctuation marks.

VB
<Extension()> 
Public Sub PrintAndPunctuate(ByVal aString As String, 
                             ByVal punc As String)
    Console.WriteLine(aString & punc)
End Sub

The method is called by sending in a string argument for punc: example.PrintAndPunctuate(".")

The following example shows Print and PrintAndPunctuate defined and called. System.Runtime.CompilerServices is imported in the definition module in order to enable access to the extension attribute.

Code

VB
Imports System.Runtime.CompilerServices  
  
Module StringExtensions  
  
    <Extension()>   
    Public Sub Print(ByVal aString As String)  
        Console.WriteLine(aString)  
    End Sub  
  
    <Extension()>   
    Public Sub PrintAndPunctuate(ByVal aString As String,   
                                 ByVal punc As String)  
        Console.WriteLine(aString & punc)  
    End Sub  
  
End Module  

Next, the extension methods are brought into scope and called.

VB
Imports ConsoleApplication2.StringExtensions  
Module Module1  
  
    Sub Main()  
  
        Dim example As String = "Example string"  
        example.Print()  
  
        example = "Hello"  
        example.PrintAndPunctuate(".")  
        example.PrintAndPunctuate("!!!!")  
  
    End Sub  
End Module  

Comments

All that is required to be able to run these or similar extension methods is that they be in scope. If the module that contains an extension method is in scope, it is visible in IntelliSense and can be called as if it were an ordinary instance method.

Notice that when the methods are invoked, no argument is sent in for the first parameter. Parameter aString in the previous method definitions is bound to example, the instance of String that calls them. The compiler will use example as the argument sent to the first parameter.

If an extension method is called for an object that is set to Nothing, the extension method executes. This does not apply to ordinary instance methods. You can explicitly check for Nothing in the extension method.

Types That Can Be Extended

You can define an extension method on most types that can be represented in a Visual Basic parameter list, including the following:

  • Classes (reference types)

  • Structures (value types)

  • Interfaces

  • Delegates

  • ByRef and ByVal arguments

  • Generic method parameters

  • Arrays

Because the first parameter specifies the data type that the extension method extends, it is required and cannot be optional. For that reason, Optional parameters and ParamArray parameters cannot be the first parameter in the parameter list.

Extension methods are not considered in late binding. In the following example, the statement anObject.PrintMe() raises a MissingMemberException exception, the same exception you would see if the second PrintMe extension method definition were deleted.

VB
Option Strict Off
Imports System.Runtime.CompilerServices

Module Module4

    Sub Main()
        Dim aString As String = "Initial value for aString"
        aString.PrintMe()

        Dim anObject As Object = "Initial value for anObject"
        ' The following statement causes a run-time error when Option
        ' Strict is off, and a compiler error when Option Strict is on.
        'anObject.PrintMe()
    End Sub

    <Extension()> 
    Public Sub PrintMe(ByVal str As String)
        Console.WriteLine(str)
    End Sub

    <Extension()> 
    Public Sub PrintMe(ByVal obj As Object)
        Console.WriteLine(obj)
    End Sub

End Module

Best Practices

Extension methods provide a convenient and powerful way to extend an existing type. However, to use them successfully, there are some points to consider. These considerations apply mainly to authors of class libraries, but they might affect any application that uses extension methods.

Most generally, extension methods that you add to types that you do not own are more vulnerable than extension methods added to types that you control. A number of things can occur in classes you do not own that can interfere with your extension methods.

  • If any accessible instance member exists that has a signature that is compatible with the arguments in the calling statement, with no narrowing conversions required from argument to parameter, the instance method will be used in preference to any extension method. Therefore, if an appropriate instance method is added to a class at some point, an existing extension member that you rely on may become inaccessible.

  • The author of an extension method cannot prevent other programmers from writing conflicting extension methods that may have precedence over the original extension.

  • You can improve robustness by putting extension methods in their own namespace. Consumers of your library can then include a namespace or exclude it, or select among namespaces, separately from the rest of the library.

  • It may be safer to extend interfaces than it is to extend classes, especially if you do not own the interface or class. A change in an interface affects every class that implements it. Therefore, the author may be less likely to add or change methods in an interface. However, if a class implements two interfaces that have extension methods with the same signature, neither extension method is visible.

  • Extend the most specific type you can. In a hierarchy of types, if you select a type from which many other types are derived, there are layers of possibilities for the introduction of instance methods or other extension methods that might interfere with yours.

Extension Methods, Instance Methods, and Properties

When an in-scope instance method has a signature that is compatible with the arguments of a calling statement, the instance method is chosen in preference to any extension method. The instance method has precedence even if the extension method is a better match. In the following example, ExampleClass contains an instance method named ExampleMethod that has one parameter of type Integer. Extension method ExampleMethod extends ExampleClass, and has one parameter of type Long.

VB
Class ExampleClass
    ' Define an instance method named ExampleMethod.
    Public Sub ExampleMethod(ByVal m As Integer)
        Console.WriteLine("Instance method")
    End Sub
End Class

<Extension()> 
Sub ExampleMethod(ByVal ec As ExampleClass, 
                  ByVal n As Long)
    Console.WriteLine("Extension method")
End Sub

The first call to ExampleMethod in the following code calls the extension method, because arg1 is Long and is compatible only with the Long parameter in the extension method. The second call to ExampleMethod has an Integer argument, arg2, and it calls the instance method.

VB
Sub Main()
    Dim example As New ExampleClass
    Dim arg1 As Long = 10
    Dim arg2 As Integer = 5

    ' The following statement calls the extension method.
    example.exampleMethod(arg1)
    ' The following statement calls the instance method.
    example.exampleMethod(arg2)
End Sub

Now reverse the data types of the parameters in the two methods:

VB
Class ExampleClass
    ' Define an instance method named ExampleMethod.
    Public Sub ExampleMethod(ByVal m As Long)
        Console.WriteLine("Instance method")
    End Sub
End Class

<Extension()> 
Sub ExampleMethod(ByVal ec As ExampleClass, 
                  ByVal n As Integer)
    Console.WriteLine("Extension method")
End Sub

This time the code in Main calls the instance method both times. This is because both arg1 and arg2 have a widening conversion to Long, and the instance method takes precedence over the extension method in both cases.

VB
Sub Main()
    Dim example As New ExampleClass
    Dim arg1 As Long = 10
    Dim arg2 As Integer = 5

    ' The following statement calls the instance method.
    example.ExampleMethod(arg1)
    ' The following statement calls the instance method.
    example.ExampleMethod(arg2)
End Sub

Therefore, an extension method cannot replace an existing instance method. However, when an extension method has the same name as an instance method but the signatures do not conflict, both methods can be accessed. For example, if class ExampleClass contains a method named ExampleMethod that takes no arguments, extension methods with the same name but different signatures are permitted, as shown in the following code.

VB
Imports System.Runtime.CompilerServices

Module Module3

    Sub Main()
        Dim ex As New ExampleClass
        ' The following statement calls the extension method.
        ex.ExampleMethod("Extension method")
        ' The following statement calls the instance method.
        ex.ExampleMethod()
    End Sub

    Class ExampleClass
        ' Define an instance method named ExampleMethod.
        Public Sub ExampleMethod()
            Console.WriteLine("Instance method")
        End Sub
    End Class

    <Extension()> 
    Sub ExampleMethod(ByVal ec As ExampleClass, 
                  ByVal stringParameter As String)
        Console.WriteLine(stringParameter)
    End Sub

End Module

The output from this code is as follows:

Extension method

Instance method

The situation is simpler with properties: if an extension method has the same name as a property of the class it extends, the extension method is not visible and cannot be accessed.

Extension Method Precedence

When two extension methods that have identical signatures are in scope and accessible, the one with higher precedence will be invoked. An extension method's precedence is based on the mechanism used to bring the method into scope. The following list shows the precedence hierarchy, from highest to lowest.

  1. Extension methods defined inside the current module.

  2. Extension methods defined inside data types in the current namespace or any one of its parents, with child namespaces having higher precedence than parent namespaces.

  3. Extension methods defined inside any type imports in the current file.

  4. Extension methods defined inside any namespace imports in the current file.

  5. Extension methods defined inside any project-level type imports.

  6. Extension methods defined inside any project-level namespace imports.

If precedence does not resolve the ambiguity, you can use the fully qualified name to specify the method that you are calling. If the Print method in the earlier example is defined in a module named StringExtensions, the fully qualified name is StringExtensions.Print(example) instead of example.Print().

See also

How to: Write an Extension Method

Extension methods enable you to add methods to an existing class. The extension method can be called as if it were an instance of that class.

To define an extension method

  1. Open a new or existing Visual Basic application in Visual Studio.

  2. At the top of the file in which you want to define an extension method, include the following import statement:

    VB
    Imports System.Runtime.CompilerServices
    
  3. Within a module in your new or existing application, begin the method definition with the extension attribute:

    VB
    <Extension()>
    
  4. Declare your method in the ordinary way, except that the type of the first parameter must be the data type you want to extend.

    VB
    <Extension()>
    Public Sub SubName (ByVal para1 As ExtendedType, <other parameters>)
         ' < Body of the method >
    End Sub
    

Example

The following example declares an extension method in module StringExtensions. A second module, Module1, imports StringExtensions and calls the method. The extension method must be in scope when it is called. Extension method PrintAndPunctuate extends the String class with a method that displays the string instance followed by a string of punctuation symbols sent in as a parameter.

VB
' Declarations will typically be in a separate module.
Imports System.Runtime.CompilerServices

Module StringExtensions
    <Extension()>
    Public Sub PrintAndPunctuate(ByVal aString As String,
                                 ByVal punc As String)
        Console.WriteLine(aString & punc)
    End Sub

End Module
VB
' Import the module that holds the extension method you want to use,
' and call it.

Imports ConsoleApplication2.StringExtensions

Module Module1
  
    Sub Main()
        Dim example = "Hello"
        example.PrintAndPunctuate("?")
        example.PrintAndPunctuate("!!!!")
    End Sub
    
End Module

Notice that the method is defined with two parameters and called with only one. The first parameter, aString, in the method definition is bound to example, the instance of String that calls the method. The output of the example is as follows:

Hello?

Hello!!!!

See also

How to: Call an Extension Method

Extension methods enable you to add methods to an existing class. After an extension method is declared and brought into scope, you can call it like an instance method of the type that it extends. For more information about how to write an extension method, see How to: Write an Extension Method.

The following instructions refer to extension method PrintAndPunctuate, which will display the string instance that invokes it, followed by whatever value is sent in for the second parameter, punc.

VB
Imports System.Runtime.CompilerServices

Module StringExtensions
    <Extension()>
    Public Sub PrintAndPunctuate(ByVal aString As String,
                                 ByVal punc As String)
        Console.WriteLine(aString & punc)
    End Sub

End Module

The method must be in scope when it is called.

To call an extension method

  1. Declare a variable that has the data type of the first parameter of the extension method. For PrintAndPunctuate, you need a String variable:

    VB
    Dim example = "Ready"
    
  2. That variable will invoke the extension method, and its value is bound to the first parameter, aString. The following calling statement will display Ready?.

    VB
    example.PrintAndPunctuate("?")
    

    Notice that the call to this extension method looks just like a call to any one of the String instance methods that require one parameter:

    VB
    example.EndsWith("dy")
    example.IndexOf("R")
    
  3. Declare another string variable and call the method again to see that it works with any string.

    VB
    Dim example2 = " or not"
    example2.PrintAndPunctuate("!!!")
    

    The result this time is: or not!!!.

Example

The following code is a complete example of the creation and use of a simple extension method.

VB
Imports System.Runtime.CompilerServices
Imports ConsoleApplication1.StringExtensions

Module Module1

    Sub Main()

        Dim example = "Hello"
        example.PrintAndPunctuate(".")
        example.PrintAndPunctuate("!!!!")

        Dim example2 = "Goodbye"
        example2.PrintAndPunctuate("?")
    End Sub

    <Extension()>
    Public Sub PrintAndPunctuate(ByVal aString As String,
                                 ByVal punc As String)
        Console.WriteLine(aString & punc)
    End Sub
End Module

' Output:
' Hello.
' Hello!!!!
' Goodbye?

See also

Lambda Expressions

A lambda expression is a function or subroutine without a name that can be used wherever a delegate is valid. Lambda expressions can be functions or subroutines and can be single-line or multi-line. You can pass values from the current scope to a lambda expression.

Note

The RemoveHandler statement is an exception. You cannot pass a lambda expression in for the delegate parameter of RemoveHandler.

You create lambda expressions by using the Function or Sub keyword, just as you create a standard function or subroutine. However, lambda expressions are included in a statement.

The following example is a lambda expression that increments its argument and returns the value. The example shows both the single-line and multi-line lambda expression syntax for a function.

VB
Dim increment1 = Function(x) x + 1
Dim increment2 = Function(x)
                     Return x + 2
                 End Function

' Write the value 2.
Console.WriteLine(increment1(1))

' Write the value 4.
Console.WriteLine(increment2(2))

The following example is a lambda expression that writes a value to the console. The example shows both the single-line and multi-line lambda expression syntax for a subroutine.

VB
Dim writeline1 = Sub(x) Console.WriteLine(x)
Dim writeline2 = Sub(x)
                     Console.WriteLine(x)
                 End Sub

' Write "Hello".
writeline1("Hello")

' Write "World"
writeline2("World")

Notice that in the previous examples the lambda expressions are assigned to a variable name. Whenever you refer to the variable, you invoke the lambda expression. You can also declare and invoke a lambda expression at the same time, as shown in the following example.

VB
Console.WriteLine((Function(num As Integer) num + 1)(5))

A lambda expression can be returned as the value of a function call (as is shown in the example in the Context section later in this topic), or passed in as an argument to a parameter that takes a delegate type, as shown in the following example.

VB
Module Module2

    Sub Main()
        ' The following line will print Success, because 4 is even.
        testResult(4, Function(num) num Mod 2 = 0)
        ' The following line will print Failure, because 5 is not > 10.
        testResult(5, Function(num) num > 10)
    End Sub

    ' Sub testResult takes two arguments, an integer value and a 
    ' delegate function that takes an integer as input and returns
    ' a boolean. 
    ' If the function returns True for the integer argument, Success
    ' is displayed.
    ' If the function returns False for the integer argument, Failure
    ' is displayed.
    Sub testResult(ByVal value As Integer, ByVal fun As Func(Of Integer, Boolean))
        If fun(value) Then
            Console.WriteLine("Success")
        Else
            Console.WriteLine("Failure")
        End If
    End Sub

End Module

Lambda Expression Syntax

The syntax of a lambda expression resembles that of a standard function or subroutine. The differences are as follows:

  • A lambda expression does not have a name.

  • Lambda expressions cannot have modifiers, such as Overloads or Overrides.

  • Single-line lambda functions do not use an As clause to designate the return type. Instead, the type is inferred from the value that the body of the lambda expression evaluates to. For example, if the body of the lambda expression is cust.City = "London", its return type is Boolean.

  • In multi-line lambda functions, you can either specify a return type by using an As clause, or omit the As clause so that the return type is inferred. When the As clause is omitted for a multi-line lambda function, the return type is inferred to be the dominant type from all the Return statements in the multi-line lambda function. The dominant type is a unique type that all other types can widen to. If this unique type cannot be determined, the dominant type is the unique type that all other types in the array can narrow to. If neither of these unique types can be determined, the dominant type is Object. In this case, if Option Strict is set to On, a compiler error occurs.

    For example, if the expressions supplied to the Return statement contain values of type Integer, Long, and Double, the resulting array is of type Double. Both Integer and Long widen to Double and only Double. Therefore, Double is the dominant type. For more information, see Widening and Narrowing Conversions.

  • The body of a single-line function must be an expression that returns a value, not a statement. There is no Return statement for single-line functions. The value returned by the single-line function is the value of the expression in the body of the function.

  • The body of a single-line subroutine must be single-line statement.

  • Single-line functions and subroutines do not include an End Function or End Sub statement.

  • You can specify the data type of a lambda expression parameter by using the As keyword, or the data type of the parameter can be inferred. Either all parameters must have specified data types or all must be inferred.

  • Optional and Paramarray parameters are not permitted.

  • Generic parameters are not permitted.

Async Lambdas

You can easily create lambda expressions and statements that incorporate asynchronous processing by using the Async and Await Operator keywords. For example, the following Windows Forms example contains an event handler that calls and awaits an async method, ExampleMethodAsync.

VB
Public Class Form1  
  
    Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click  
        ' ExampleMethodAsync returns a Task.  
        Await ExampleMethodAsync()  
        TextBox1.Text = vbCrLf & "Control returned to button1_Click."  
    End Sub  
  
    Async Function ExampleMethodAsync() As Task  
        ' The following line simulates a task-returning asynchronous process.  
        Await Task.Delay(1000)  
    End Function  
  
End Class  

You can add the same event handler by using an async lambda in an AddHandler Statement. To add this handler, add an Async modifier before the lambda parameter list, as the following example shows.

VB
Public Class Form1  
  
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load  
        AddHandler Button1.Click,   
            Async Sub(sender1, e1)  
                ' ExampleMethodAsync returns a Task.  
                Await ExampleMethodAsync()  
                TextBox1.Text = vbCrLf & "Control returned to Button1_ Click."  
            End Sub  
    End Sub  
  
    Async Function ExampleMethodAsync() As Task  
        ' The following line simulates a task-returning asynchronous process.  
        Await Task.Delay(1000)  
    End Function  
  
End Class  

For more information about how to create and use async methods, see Asynchronous Programming with Async and Await.

Context

A lambda expression shares its context with the scope within which it is defined. It has the same access rights as any code written in the containing scope. This includes access to member variables, functions and subs, Me, and parameters and local variables in the containing scope.

Access to local variables and parameters in the containing scope can extend beyond the lifetime of that scope. As long as a delegate referring to a lambda expression is not available to garbage collection, access to the variables in the original environment is retained. In the following example, variable target is local to makeTheGame, the method in which the lambda expression playTheGame is defined. Note that the returned lambda expression, assigned to takeAGuess in Main, still has access to the local variable target.

VB
Module Module6

    Sub Main()
        ' Variable takeAGuess is a Boolean function. It stores the target
        ' number that is set in makeTheGame.
        Dim takeAGuess As gameDelegate = makeTheGame()

        ' Set up the loop to play the game.
        Dim guess As Integer
        Dim gameOver = False
        While Not gameOver
            guess = CInt(InputBox("Enter a number between 1 and 10 (0 to quit)", "Guessing Game", "0"))
            ' A guess of 0 means you want to give up.
            If guess = 0 Then
                gameOver = True
            Else
                ' Tests your guess and announces whether you are correct. Method takeAGuess
                ' is called multiple times with different guesses. The target value is not 
                ' accessible from Main and is not passed in.
                gameOver = takeAGuess(guess)
                Console.WriteLine("Guess of " & guess & " is " & gameOver)
            End If
        End While

    End Sub

    Delegate Function gameDelegate(ByVal aGuess As Integer) As Boolean

    Public Function makeTheGame() As gameDelegate

        ' Generate the target number, between 1 and 10. Notice that 
        ' target is a local variable. After you return from makeTheGame,
        ' it is not directly accessible.
        Randomize()
        Dim target As Integer = CInt(Int(10 * Rnd() + 1))

        ' Print the answer if you want to be sure the game is not cheating
        ' by changing the target at each guess.
        Console.WriteLine("(Peeking at the answer) The target is " & target)

        ' The game is returned as a lambda expression. The lambda expression
        ' carries with it the environment in which it was created. This 
        ' environment includes the target number. Note that only the current
        ' guess is a parameter to the returned lambda expression, not the target. 

        ' Does the guess equal the target?
        Dim playTheGame = Function(guess As Integer) guess = target

        Return playTheGame

    End Function

End Module

The following example demonstrates the wide range of access rights of the nested lambda expression. When the returned lambda expression is executed from Main as aDel, it accesses these elements:

  • A field of the class in which it is defined: aField

  • A property of the class in which it is defined: aProp

  • A parameter of method functionWithNestedLambda, in which it is defined: level1

  • A local variable of functionWithNestedLambda: localVar

  • A parameter of the lambda expression in which it is nested: level2

VB
Module Module3

    Sub Main()
        ' Create an instance of the class, with 1 as the value of 
        ' the property.
        Dim lambdaScopeDemoInstance = 
            New LambdaScopeDemoClass With {.Prop = 1}

        ' Variable aDel will be bound to the nested lambda expression  
        ' returned by the call to functionWithNestedLambda.
        ' The value 2 is sent in for parameter level1.
        Dim aDel As aDelegate = 
            lambdaScopeDemoInstance.functionWithNestedLambda(2)

        ' Now the returned lambda expression is called, with 4 as the 
        ' value of parameter level3.
        Console.WriteLine("First value returned by aDel:   " & aDel(4))

        ' Change a few values to verify that the lambda expression has 
        ' access to the variables, not just their original values.
        lambdaScopeDemoInstance.aField = 20
        lambdaScopeDemoInstance.Prop = 30
        Console.WriteLine("Second value returned by aDel: " & aDel(40))
    End Sub

    Delegate Function aDelegate(
        ByVal delParameter As Integer) As Integer

    Public Class LambdaScopeDemoClass
        Public aField As Integer = 6
        Dim aProp As Integer

        Property Prop() As Integer
            Get
                Return aProp
            End Get
            Set(ByVal value As Integer)
                aProp = value
            End Set
        End Property

        Public Function functionWithNestedLambda(
            ByVal level1 As Integer) As aDelegate

            Dim localVar As Integer = 5

            ' When the nested lambda expression is executed the first 
            ' time, as aDel from Main, the variables have these values:
            ' level1 = 2
            ' level2 = 3, after aLambda is called in the Return statement
            ' level3 = 4, after aDel is called in Main
            ' localVar = 5
            ' aField = 6
            ' aProp = 1
            ' The second time it is executed, two values have changed:
            ' aField = 20
            ' aProp = 30
            ' level3 = 40
            Dim aLambda = Function(level2 As Integer) _
                              Function(level3 As Integer) _
                                  level1 + level2 + level3 + localVar +
                                    aField + aProp

            ' The function returns the nested lambda, with 3 as the 
            ' value of parameter level2.
            Return aLambda(3)
        End Function

    End Class
End Module

Converting to a Delegate Type

A lambda expression can be implicitly converted to a compatible delegate type. For information about the general requirements for compatibility, see Relaxed Delegate Conversion. For example, the following code example shows a lambda expression that implicitly converts to Func(Of Integer, Boolean) or a matching delegate signature.

VB
' Explicitly specify a delegate type.
Delegate Function MultipleOfTen(ByVal num As Integer) As Boolean

' This function matches the delegate type.
Function IsMultipleOfTen(ByVal num As Integer) As Boolean
    Return num Mod 10 = 0
End Function

' This method takes an input parameter of the delegate type. 
' The checkDelegate parameter could also be of 
' type Func(Of Integer, Boolean).
Sub CheckForMultipleOfTen(ByVal values As Integer(),
                          ByRef checkDelegate As MultipleOfTen)
    For Each value In values
        If checkDelegate(value) Then
            Console.WriteLine(value & " is a multiple of ten.")
        Else
            Console.WriteLine(value & " is not a multiple of ten.")
        End If
    Next
End Sub

' This method shows both an explicitly defined delegate and a
' lambda expression passed to the same input parameter.
Sub CheckValues()
    Dim values = {5, 10, 11, 20, 40, 30, 100, 3}
    CheckForMultipleOfTen(values, AddressOf IsMultipleOfTen)
    CheckForMultipleOfTen(values, Function(num) num Mod 10 = 0)
End Sub

The following code example shows a lambda expression that implicitly converts to Sub(Of Double, String, Double) or a matching delegate signature.

VB
Module Module1
    Delegate Sub StoreCalculation(ByVal value As Double,
                                  ByVal calcType As String,
                                  ByVal result As Double)

    Sub Main()
        ' Create a DataTable to store the data.
        Dim valuesTable = New DataTable("Calculations")
        valuesTable.Columns.Add("Value", GetType(Double))
        valuesTable.Columns.Add("Calculation", GetType(String))
        valuesTable.Columns.Add("Result", GetType(Double))

        ' Define a lambda subroutine to write to the DataTable.
        Dim writeToValuesTable = Sub(value As Double, calcType As String, result As Double)
                                     Dim row = valuesTable.NewRow()
                                     row(0) = value
                                     row(1) = calcType
                                     row(2) = result
                                     valuesTable.Rows.Add(row)
                                 End Sub

        ' Define the source values.
        Dim s = {1, 2, 3, 4, 5, 6, 7, 8, 9}

        ' Perform the calculations.
        Array.ForEach(s, Sub(c) CalculateSquare(c, writeToValuesTable))
        Array.ForEach(s, Sub(c) CalculateSquareRoot(c, writeToValuesTable))

        ' Display the data.
        Console.WriteLine("Value" & vbTab & "Calculation" & vbTab & "Result")
        For Each row As DataRow In valuesTable.Rows
            Console.WriteLine(row(0).ToString() & vbTab &
                              row(1).ToString() & vbTab &
                              row(2).ToString())
        Next

    End Sub


    Sub CalculateSquare(ByVal number As Double, ByVal writeTo As StoreCalculation)
        writeTo(number, "Square     ", number ^ 2)
    End Sub

    Sub CalculateSquareRoot(ByVal number As Double, ByVal writeTo As StoreCalculation)
        writeTo(number, "Square Root", Math.Sqrt(number))
    End Sub
End Module

When you assign lambda expressions to delegates or pass them as arguments to procedures, you can specify the parameter names but omit their data types, letting the types be taken from the delegate.

Examples

  • The following example defines a lambda expression that returns True if the nullable argument has an assigned value, and False if its value is Nothing.

    VB
    Dim notNothing =
      Function(num? As Integer) num IsNot Nothing
    Dim arg As Integer = 14
    Console.WriteLine("Does the argument have an assigned value?")
    Console.WriteLine(notNothing(arg))
    
  • The following example defines a lambda expression that returns the index of the last element in an array.

    VB
    Dim numbers() = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    Dim lastIndex =
      Function(intArray() As Integer) intArray.Length - 1
    For i = 0 To lastIndex(numbers)
        numbers(i) += 1
    Next
    

See also

How to: Create a Lambda Expression

A lambda expression is a function or subroutine that does not have a name. A lambda expression can be used wherever a delegate type is valid.

To create a single-line lambda expression function

  1. In any situation where a delegate type could be used, type the keyword Function, as in the following example:

    Dim add1 = Function

  2. In parentheses, directly after Function, type the parameters of the function. Notice that you do not specify a name after Function.

    Dim add1 = Function (num As Integer)

  3. Following the parameter list, type a single expression as the body of the function. The value that the expression evaluates to is the value returned by the function. You do not use an As clause to specify the return type.

    VB
    Dim add1 = Function(num As Integer) num + 1
    

    You call the lambda expression by passing in an integer argument.

    VB
    ' The following line prints 6.
    Console.WriteLine(add1(5))
    
  4. Alternatively, the same result is accomplished by the following example:

    VB
    Console.WriteLine((Function(num As Integer) num + 1)(5))
    

To create a single-line lambda expression subroutine

  1. In any situation where a delegate type could be used, type the keyword Sub, as shown in the following example.

    Dim add1 = Sub

  2. In parentheses, directly after Sub, type the parameters of the subroutine. Notice that you do not specify a name after Sub.

    Dim add1 = Sub (msg As String)

  3. Following the parameter list, type a single statement as the body of the subroutine.

    VB
    Dim writeMessage = Sub(msg As String) Console.WriteLine(msg)
    

    You call the lambda expression by passing in a string argument.

    VB
    ' The following line prints "Hello".
    writeMessage("Hello")
    

To create a multiline lambda expression function

  1. In any situation where a delegate type could be used, type the keyword Function, as shown in the following example.

    Dim add1 = Function

  2. In parentheses, directly after Function, type the parameters of the function. Notice that you do not specify a name after Function.

    Dim add1 = Function (index As Integer)

  3. Press ENTER. The End Function statement is automatically added.

  4. Within the body of the function, add the following code to create an expression and return the value. You do not use an As clause to specify the return type.

    VB
    Dim getSortColumn = Function(index As Integer)
                            Select Case index
                                Case 0
                                    Return "FirstName"
                                Case 1
                                    Return "LastName"
                                Case 2
                                    Return "CompanyName"
                                Case Else
                                    Return "LastName"
                            End Select
                        End Function
    

    You call the lambda expression by passing in an integer argument.

    VB
    Dim sortColumn = getSortColumn(0)
    

To create a multiline lambda expression subroutine

  1. In any situation where a delegate type could be used, type the keyword Sub, as shown in the following example:

    Dim add1 = Sub

  2. In parentheses, directly after Sub, type the parameters of the subroutine. Notice that you do not specify a name after Sub.

    Dim add1 = Sub (msg As String)

  3. Press ENTER. The End Sub statement is automatically added.

  4. Within the body of the function, add the following code to execute when the subroutine is invoked.

    VB
    Dim writeToLog = Sub(msg As String)
                         Dim log As New EventLog()
                         log.Source = "Application"
                         log.WriteEntry(msg)
                         log.Close()
                     End Sub
    

    You call the lambda expression by passing in a string argument.

    VB
    writeToLog("Application started.")
    

Example

A common use of lambda expressions is to define a function that can be passed in as the argument for a parameter whose type is Delegate. In the following example, the GetProcesses method returns an array of the processes running on the local computer. The Where method from the Enumerable class requires a Boolean delegate as its argument. The lambda expression in the example is used for that purpose. It returns True for each process that has only one thread, and those are selected in filteredList.

VB
Sub Main()

    ' Create an array of running processes.
    Dim procList As Process() = Diagnostics.Process.GetProcesses

    ' Return the processes that have one thread. Notice that the type
    ' of the parameter does not have to be explicitly stated.
    Dim filteredList = procList.Where(Function(p) p.Threads.Count = 1)

    ' Display the name of each selected process.
    For Each proc In filteredList
        MsgBox(proc.ProcessName)
    Next

End Sub

The previous example is equivalent to the following code, which is written in Language-Integrated Query (LINQ) syntax:

VB
Sub Main()

    Dim filteredQuery = From proc In Diagnostics.Process.GetProcesses
                        Where proc.Threads.Count = 1
                        Select proc

    For Each proc In filteredQuery
        MsgBox(proc.ProcessName)
    Next
End Sub

See also

Partial Methods

Partial methods enable developers to insert custom logic into code. Typically, the code is part of a designer-generated class. Partial methods are defined in a partial class that is created by a code generator, and they are commonly used to provide notification that something has been changed. They enable the developer to specify custom behavior in response to the change.

The designer of the code generator defines only the method signature and one or more calls to the method. Developers can then provide implementations for the method if they want to customize the behavior of the generated code. When no implementation is provided, calls to the method are removed by the compiler, resulting in no additional performance overhead.

Declaration

The generated code marks the definition of a partial method by placing the keyword Partial at the start of the signature line.

VB
Partial Private Sub QuantityChanged()  
End Sub  

The definition must meet the following conditions:

  • The method must be a Sub, not a Function.

  • The body of the method must be left empty.

  • The access modifier must be Private.

Implementation

The implementation consists primarily of filling in the body of the partial method. The implementation is typically in a separate partial class from the definition, and is written by a developer who wants to extend the generated code.

VB
Private Sub QuantityChanged()  
'    Code for executing the desired action.  
End Sub  

The previous example duplicates the signature in the declaration exactly, but variations are possible. In particular, other modifiers can be added, such as Overloads or Overrides. Only one Overrides modifier is permitted. For more information about method modifiers, see Sub Statement.

Use

You call a partial method as you would call any other Sub procedure. If the method has been implemented, the arguments are evaluated and the body of the method is executed. However, remember that implementing a partial method is optional. If the method is not implemented, a call to it has no effect, and expressions passed as arguments to the method are not evaluated.

Example

In a file named Product.Designer.vb, define a Product class that has a Quantity property.

VB
Partial Class Product

    Private _Quantity As Integer

    Property Quantity() As Integer
        Get
            Return _Quantity
        End Get
        Set(ByVal value As Integer)
            _Quantity = value
            QuantityChanged()
        End Set
    End Property

    ' Provide a signature for the partial method.
    Partial Private Sub QuantityChanged()
    End Sub
End Class

In a file named Product.vb, provide an implementation for QuantityChanged.

VB
Partial Class Product

    Private Sub QuantityChanged()
        MsgBox("Quantity was changed to " & Me.Quantity)
    End Sub

End Class

Finally, in the Main method of a project, declare a Product instance and provide an initial value for its Quantity property.

VB
Module Module1

    Sub Main()
        Dim product1 As New Product With {.Quantity = 100}
    End Sub

End Module

A message box should appear that displays this message:

Quantity was changed to 100

See also

 

Source/Reference


©sideway

ID: 201000008 Last Updated: 8/10/2020 Revision: 0 Ref:

close

References

  1. Active Server Pages,  , http://msdn.microsoft.com/en-us/library/aa286483.aspx
  2. ASP Overview,  , http://msdn.microsoft.com/en-us/library/ms524929%28v=vs.90%29.aspx
  3. ASP Best Practices,  , http://technet.microsoft.com/en-us/library/cc939157.aspx
  4. ASP Built-in Objects,  , http://msdn.microsoft.com/en-us/library/ie/ms524716(v=vs.90).aspx
  5. Response Object,  , http://msdn.microsoft.com/en-us/library/ms525405(v=vs.90).aspx
  6. Request Object,  , http://msdn.microsoft.com/en-us/library/ms524948(v=vs.90).aspx
  7. Server Object (IIS),  , http://msdn.microsoft.com/en-us/library/ms525541(v=vs.90).aspx
  8. Application Object (IIS),  , http://msdn.microsoft.com/en-us/library/ms525360(v=vs.90).aspx
  9. Session Object (IIS),  , http://msdn.microsoft.com/en-us/library/ms524319(8v=vs.90).aspx
  10. ASPError Object,  , http://msdn.microsoft.com/en-us/library/ms524942(v=vs.90).aspx
  11. ObjectContext Object (IIS),  , http://msdn.microsoft.com/en-us/library/ms525667(v=vs.90).aspx
  12. Debugging Global.asa Files,  , http://msdn.microsoft.com/en-us/library/aa291249(v=vs.71).aspx
  13. How to: Debug Global.asa files,  , http://msdn.microsoft.com/en-us/library/ms241868(v=vs.80).aspx
  14. Calling COM Components from ASP Pages,  , http://msdn.microsoft.com/en-us/library/ms524620(v=VS.90).aspx
  15. IIS ASP Scripting Reference,  , http://msdn.microsoft.com/en-us/library/ms524664(v=vs.90).aspx
  16. ASP Keywords,  , http://msdn.microsoft.com/en-us/library/ms524672(v=vs.90).aspx
  17. Creating Simple ASP Pages,  , http://msdn.microsoft.com/en-us/library/ms524741(v=vs.90).aspx
  18. Including Files in ASP Applications,  , http://msdn.microsoft.com/en-us/library/ms524876(v=vs.90).aspx
  19. ASP Overview,  , http://msdn.microsoft.com/en-us/library/ms524929(v=vs.90).aspx
  20. FileSystemObject Object,  , http://msdn.microsoft.com/en-us/library/z9ty6h50(v=vs.84).aspx
  21. http://msdn.microsoft.com/en-us/library/windows/desktop/ms675944(v=vs.85).aspx,  , ADO Object Model
  22. ADO Fundamentals,  , http://msdn.microsoft.com/en-us/library/windows/desktop/ms680928(v=vs.85).aspx
close
IMAGE

Home 5

Business

Management

HBR 3

Information

Recreation

Hobbies 8

Culture

Chinese 1097

English 337

Reference 67

Computer

Hardware 151

Software

Application 198

Digitization 25

Latex 9

Manim 121

Numeric 19

Programming

Web 283

Unicode 494

HTML 65

CSS 58

ASP.NET 174

OS 389

DeskTop 7

Python 19

Knowledge

Mathematics

Formulas 8

Algebra 25

Number Theory 206

Trigonometry 18

Geometry 21

Calculus 67

Complex Analysis 21

Engineering

Tables 8

Mechanical

Mechanics 1

Rigid Bodies

Statics 92

Dynamics 37

Fluid 5

Fluid Kinematics 5

Control

Process Control 1

Acoustics 19

FiniteElement 2

Physics

Electric 27

Biology 1

Geography 1


Copyright © 2000-2020 Sideway . All rights reserved Disclaimers last modified on 06 September 2019