Chapter 17. Subroutines and Functions

Subroutines and functions enable you to break an otherwise unwieldy chunk of code into manageable pieces. They allow you to extract code that you may need to use under more than one circumstance and place it in one location where you can call it as needed. This not only reduces repetition within your code; it also enables you to maintain and update the code in a single location.

A subroutine performs a task for the code that invokes it. A function performs a task and then returns some value. The value may be the result of a calculation, or a status code indicating whether the function succeeded or failed.

Together, subroutines and functions are sometimes called routines or procedures. They are also sometimes called methods, particularly when they are subroutines or functions belonging to a class. Subroutines are also occasionally called sub procedures or less formally subs.

This chapter describes subroutines and functions. It explains the syntax for declaring and using each in a Visual Basic application. It also provides some tips for making routines more maintainable.

SUBROUTINES

A Sub statement defines the subroutine's name. It declares the parameters that the subroutine takes as arguments and defines the parameters' data types. Code between the Sub statement and an End Sub statement determines what the subroutine does when it runs.

The syntax for defining a subroutine is as follows:

[attribute_list] [inheritance_mode] [accessibility] _
Sub subroutine_name([parameters]) [ Implements interface.subroutine ]
    [ statements ]
End Sub

The following sections describe the pieces of this declaration.

Attribute_List

The optional attribute list is a comma-separated list of attributes that apply to the subroutine. An attribute further refines the definition of a class, method, variable, or other item to give more information to the compiler and the runtime system.

Attributes are specialized and address issues that arise when you perform very specific programming tasks. For example, the Conditional attribute means the subroutine is conditional upon the definition of some compiler constant. Example program AttributeConditional uses the following code to demonstrate the Conditional attribute:

#Const DEBUG_LIST_CUSTOMERS = True
' #Const DEBUG_LIST_EMPLOYEES = True

Private Sub Form1_Load() Handles MyBase.Load
    ListCustomers()
    ListEmployees()

    txtResults.Select(0, 0)
End Sub

<Conditional("DEBUG_LIST_CUSTOMERS")>
Private Sub ListCustomers()
    txtResults.Text &= "ListCustomers" & vbCrLf
End Sub

<Conditional("DEBUG_LIST_EMPLOYEES")>
Private Sub ListEmployees()
    txtResults.Text &= "ListEmployees" & vbCrLf
End Sub
                                                  
DELIGHTFUL DECORATIONS

The code defines the compiler constant DEBUG_LIST_CUSTOMERS. The value DEBUG_LIST_EMPLOYEES is not defined because it is commented out.

This program's Form1_Load event handler calls subroutines ListCustomers and ListEmployees. ListCustomers is defined using the Conditional attribute with parameter DEBUG_LIST_CUSTOMERS. That tells the compiler to generate code for the routine only if DEBUG_LIST_CUSTOMERS is defined. Because that constant is defined, the compiler generates code for this subroutine.

Subroutine ListEmployees is defined using the Conditional attribute with parameter DEBUG_LIST_EMPLOYEES. Because that constant is not defined, the compiler does not generate code for this subroutine and, when Form1_Load calls it, the subroutine call is ignored.

The following text shows the output from this program:

ListCustomers

Visual Basic 2010 defines more than 400 attributes. Many have very specialized purposes that won't interest you most of the time, but some are pretty useful. For example, the Browsable attribute determines whether a property or event should be listed in the Properties window. It is fairly general and useful, so it's described shortly. In contrast, the System.EnterpriseServices.ApplicationQueuing attribute enables queuing for an assembly and allows it to read method calls from message queues. This attribute is only useful in very specialized circumstances, so it isn't described here.

Many attributes give metadata for editors and the IDE, so you will often see their effects only when you view an object in an editor or the IDE. If you are building a control or component, you can put one on a form and then see its properties in the Properties window. In that case, many kinds of attributes will be useful. If you're building an Employee class that's used only in code, fewer attributes are useful in any obvious way.

However, Visual Basic comes with a powerful PropertyGrid control that lets you display an object's properties on a form much as the Properties window displays them to a developer. That control honors all of the property-related attributes and gives them a whole new level of usefulness.

The following list describes some of the most useful attributes. Most of them are in the System.ComponentModel namespace. Check the online help to find the namespaces for the others and to learn about each attribute's parameters. Even these most useful attributes are fairly specialized and advanced so you may not immediately see their usefulness. If one of them doesn't make sense, skip it and scan the list again after you have more experience with such topics as building custom controls.

  • AttributeUsage — You can build your own custom attributes by inheriting from the Attribute class. You can give your attribute class the AttributeUsage attribute to specify how your attribute can be used. You can determine whether an item can have multiple instances of your attribute, whether your attribute can be inherited by a derived class, and the kinds of things that can have your attribute (assembly, class, method, and so forth).

  • Browsable — This indicates whether a property or event should be displayed in an editor such as the Properties window. If you pass the attribute's constructor the value False, the Properties window does not display the property.

  • Category — This indicates the grouping that should hold the property or event in a visual designer such as the Properties window. For example, if the user clicks the Categorized button in the Properties window, the window groups the properties by category. This attribute tells which category should hold the property. Note that the category names are not magic. You can use any string you like and the Properties window will make a new category for you if necessary.

  • DefaultEvent — This gives a class's default event name. If the class is a control or component and you double-click it in a form, the code editor opens to this event. For example, the default event for a Button is Click, so when you double-click a Button at design time, the code editor opens the control's Click event handler.

  • DefaultProperty — This gives a class's default property name. Suppose that the Employee component has LastName set as its default property. Then suppose that you select the form and click the FormBorderStyle property in the Properties window. Now you click an Employee. Because Employee doesn't have a FormBorderStyle property, the Properties window displays its default property: LastName.

  • DefaultValue — This gives a property a default value. If you right-click the property in the Properties window and select Reset, the property is reset to this value. Be sure to use a valid value. For example, don't set this to the string "unknown" if the property is an Integer.

  • Description — This gives a description of the item. If a property has a Description and you select the property in the Properties window, the window displays the description text at the bottom.

Visual Basic carries this one step further and also allows you to use XML comments to provide a description of routines and their parameters for use by IntelliSense. For more information, see the section "XML Comments" in Chapter 14, "Program and Module Structure."

  • Localizable — This determines whether a property should be localizable so you can easily store different versions of the property for different languages and locales. If this is True, localized values are automatically stored in the appropriate resource files for different locales and automatically loaded at startup based on the user's computer settings. If this is False (the default), all locales share the same property value.

To try this out, set the form's Localizable property to True and enter a value for the property. Then set the form's Language property to another language and give the localizable property a new value. Visual Basic automatically applies the right value for the user's locale when it runs the program.

  • MergableProperty — This indicates whether or not the property can be merged with the same property provided by other components in the Properties window. If this is False and you select more than one instance of a control with the property, the Properties window does not display the property.

    If this is True and you select more than one control with the property, the Properties window displays the value if the controls all have the same value. If you enter a new value, all of the controls are updated. This is the way the Text property works for TextBox, Label, and many other kinds of controls.

  • ParenthesizePropertyName — This indicates whether editors such as the Properties window should display parentheses around the property's name. If the name has parentheses, the Properties window moves it to the top of the list when displaying properties alphabetically or to the top of its category when displaying properties by category.

  • ReadOnly — This indicates whether designers should treat this property as read-only. For example, the Properties window displays the property grayed out and doesn't let the user change its value. This attribute is a little strange in practice because ReadOnly is a Visual Basic keyword. If you enter just the attribute name ReadOnly, Visual Basic gets confused. Either use the full name System.ComponentModel.ReadOnly or enclose the name in square brackets as in <[ReadOnly](True)>. . . .

  • RecommendedAsConfigurable — This indicates that a property should be tied to the configuration file. When you select the object at design time and expand the "(Dynamic Properties)" item, the property is listed. If you click the ellipsis to the right, a dialog appears that lets you map the property to a key in the configuration file.

  • RefreshProperties — This indicates how an editor should refresh the object's other properties if this property is changed. The value can be Default (do not refresh the other properties), Repaint (refresh all other properties), or All (re-query and refresh all properties).

  • Conditional — This indicates that the method is callable if a compile-time constant such as DEBUG or MY_CONSTANT is defined. If the constant is not defined, code for the method is still generated and parameters in the method call are checked against the parameter types used by the method, but calls to the method are ignored at runtime. If the method has more than one Conditional attribute, the method is callable if any of the specified compile-time constants is defined.

Note that the constant must be defined in the main program, not in the component if you are building a component. Select the main program, open the Project menu, select the Properties item at the bottom, open the Configuration Properties folder, click Build, and in the Custom constants text box enter a value such as IS_DEFINED=True.

You can also use the compiler directive #If to exclude code completely from compilation. However, if you eliminate a method in this way, any calls to the routine will generate compile-time errors because the method doesn't exist. The Conditional attribute lets you hide a method while still allowing the code to contain calls to it.

  • DebuggerHidden — This tells debuggers whether a method should be debuggable. If DebuggerHidden is True, the IDE skips over the method and will not stop at breakpoints inside it.

  • DebuggerStepThrough — This tells debuggers whether to let the developer step through a method in the debugger. If DebuggerStepThrough is True, the IDE will not step through the method, although it will stop at any breakpoints inside it.

  • ToolboxBitmap — This tells the IDE where to find a control or component's Toolbox bitmap. This can be a file, or it can be a type in an assembly that contains the bitmap and the bitmap's name in the assembly. It's awkward but essential if you're developing controls or components.

  • NonSerializedAttribute — This indicates that a member of a serializable class should not be serialized. This is useful for excluding values that need not be serialized.

  • Obsolete — This indicates that the item (class, method, property, or whatever) is obsolete. Optionally, you can specify the message that the code editor should display to the developer if code uses the item (for example, "Use the NewMethod instead"). You can also indicate whether the IDE should treat using this item as a warning or an error.

  • Serializable — This indicates that a class is serializable. All public and private fields are serialized by default. Note that some routines require a class to be serializable even though you don't use the serialization yourself. Also note that attributes in the System.Xml.Serialization namespace can provide a lot of control over serializations.

  • ThreadStaticAttribute — This indicates that a Shared class variable should not be shared across threads. Different threads get their own copies of the variable and all instances of the class within each thread share the thread's copy.

Inheritance_Mode

The inheritance_mode can be one of the values Overloads, Overrides, Overridable, NotOverridable, MustOverride, Shadows, or Shared. These values determine how a subroutine declared within a class inherits from the parent class or how it allows inheritance in derived classes. The following list explains the meanings of these keywords:

  • Overloads — Indicates that the subroutine has the same name as another subroutine defined for this class. The parameter list must be different in the different versions so that Visual Basic can tell them apart (if they are the same, this works just like Overrides described next). If you are overloading a subroutine defined in a parent class, you must use this keyword. If you are overloading only subroutines in the same class, you can omit the keyword. If you use the keyword in any of the overloaded subroutines, however, you must include it for them all.

  • Overrides — Indicates that this subroutine replaces a subroutine in the parent class that has the same name and parameters.

  • Overridable — Indicates that a derived class can override this subroutine. This is the default for a subroutine that overrides another one.

  • NotOverridable — Indicates that a derived class cannot override this subroutine. You can only use this with a subroutine that overrides another one.

  • MustOverride — Indicates that any derived classes must override this subroutine. When you use this keyword, you omit all subroutine code and the End Sub statement, as in the following code:

    MustOverride Sub Draw()
    MustOverride Sub MoveMap(ByVal X As Integer, ByVal Y As Integer)
    MustOverride Sub Delete()
    ...

If a class contains a subroutine declared MustOverride, you must declare the class using the MustInherit keyword. Otherwise, Visual Basic won't know what to do if you call this subroutine, because it contains no code.

MustOverride is handy for defining a subroutine that derived classes must implement, but for which a default implementation in the parent class doesn't make sense. For example, suppose that you make a Drawable class that represents a shape that can be drawn and that you will derive specific shape classes such as Rectangle, Ellipse, Line, and so forth. To let the program draw a generic shape, the Drawable class defines the Draw subroutine. Because Drawable doesn't have a particular shape, it cannot provide a default implementation of that subroutine. To require the derived classes to implement Draw, the Drawable class declares it MustOverride.

  • Shadows — Indicates that this subroutine replaces an item (probably a subroutine) in the parent class that has the same name, but not necessarily the same parameters. If the parent class contains more than one overloaded version of the subroutine, this subroutine shadows them all. If the derived class defines more than one overloaded version of the subroutine, they must all be declared with the Shadows keyword.

  • Shared — Indicates that this subroutine is associated with the class itself, rather than with a specific instance of the class. You can invoke it using the class's name (ClassName.SharedSub) or using a specific instance (class_instance.SharedSub). Because the subroutine is not associated with a specific class instance, it cannot use any properties or methods that are provided by a specific instance. The subroutine can only use other Shared properties and methods, as well as globally available variables.

Accessibility

A subroutine's accessibility clause can take one of these values: Public, Protected, Friend, Protected Friend, or Private. These values determine which pieces of code can invoke the subroutine. The following list explains these keywords:

  • Public — Indicates that there are no restrictions on the subroutine. Code inside or outside of the subroutine's class or module can call it.

  • Protected — Indicates that the subroutine is accessible only to code in the same class or in a derived class. You can only use the Protected keyword with subroutines declared inside a class.

  • Friend — Indicates that the subroutine is available to all code inside or outside of the subroutine's module within the same project. The difference between this and Public is that Public allows code outside of the project to access the subroutine. This is generally only an issue for code libraries (DLLs) and control libraries. For example, suppose that you build a code library containing dozens of routines and then you write a program that uses the library. If the library declares a subroutine with the Public keyword, the code in the library and the code in the main program can use the subroutine. In contrast, if the library declares a subroutine with the Friend keyword, only the code in the library can access the subroutine, not the code in the main program.

  • Protected Friend — Indicates that the subroutine has both Protected and Friend status. The subroutine is available only within the same project and within the same class or a derived class.

  • Private — Indicates that the subroutine is available only within the class or module that contains it.

To reduce the amount of information that developers must remember, you should generally declare subroutines with the most restricted accessibility that allows them to do their jobs. If you can, declare the subroutine Private. Then, developers working on other parts of the application don't even need to know that the subroutine exists. They can create other routines with the same name if necessary and won't accidentally misuse the subroutine.

Later, if you discover that you need to use the subroutine outside of its class or module, you can change its declaration to allow greater accessibility.

Subroutine_Name

The subroutine's name must be a valid Visual Basic identifier. That means it should begin with a letter or an underscore. It can then contain zero or more letters, numbers, and underscores. If the name begins with an underscore, it must include at least one other character so that Visual Basic can tell it apart from a line continuation character.

Many developers use camel case when naming subroutines so a subroutine's name consists of several descriptive words with their first letters capitalized. A good method for generating subroutine names is to use a short phrase beginning with a verb and describing what the subroutine does. Some examples include LoadData, SaveNetworkConfiguration, and PrintExpenseReport.

Subroutine names with leading underscores can be hard to read, so you should either save them for special purposes or avoid them entirely. Names such as _1 and __ (two underscores) are particularly confusing.

Parameters

The parameters section of the subroutine declaration defines the arguments that the subroutine takes as parameters. The parameter declarations define the numbers and types of the parameters. This section also gives the names by which the subroutine will know the values.

Declaring parameters is very similar to declaring variables. See Chapter 15, "Data Types, Variables, and Constants," for information on variable declarations, data types, and other related topics.

The following sections describe some of the more important details related to subroutine parameter declarations.

ByVal

If you include the optional ByVal keyword before a parameter's declaration, the subroutine makes its own local copy of the parameter with procedure scope. The subroutine can modify this value all it wants and the corresponding value in the calling procedure isn't changed.

For example, consider the following code. The main program initializes the variable A and prints its value in the Output window. It then calls subroutine DisplayDouble, which declares its parameter X with the ByVal keyword. It doubles X and displays the new value. Because the parameter X is declared ByVal, the subroutine has its own local copy of the variable, so doubling it doesn't change the value of the variable A in the main program. When the subroutine ends and the main program resumes, it displays the value of variable A.

Private Sub Main()
    Dim A As Integer = 12
    Debug.WriteLine("Main: " & A)
    DisplayDouble(A)
    Debug.WriteLine("Main: " & A)
End Sub

Private Sub DisplayDouble(ByVal X As Integer)
    X *= 2
    Debug.WriteLine("DisplayDouble: " & X)
End Sub

The following text shows the results:

Main: 12
DisplayDouble: 24
Main: 12

ByRef

If you declare a parameter with the ByRef keyword, the subroutine does not create a separate copy of the parameter variable. Instead, it uses a reference to the original parameter passed into the subroutine and any changes the subroutine makes to the value are reflected in the calling subroutine.

Consider the following code. This code is the same as the previous example except that the DisplayDouble subroutine declares its parameter using the ByRef keyword. As before, the main program initializes the variable A and prints its value in the Output window. It then calls subroutine DisplayDouble, which doubles its parameter X and displays the new value. Because X is declared ByRef, this doubles the value of the variable A that was passed by the main program into the subroutine. When the subroutine ends and the main program resumes, it displays the new doubled value of variable A.

Private Sub Main()
    Dim A As Integer = 12
    Debug.WriteLine("Main: " & A)
    DisplayDouble(A)
    Debug.WriteLine("Main: " & A)
End Sub

Private Sub DisplayDouble(ByRef X As Integer)
    X *= 2
    Debug.WriteLine("DisplayDouble: " & X)
End Sub

The following shows the results:

Main: 12
DisplayDouble: 24
Main: 24

Arrays Declared ByVal and ByRef

If you declare an array parameter using ByVal or ByRef, those keywords apply to the array itself, not to the array's values. In either case, the subroutine can modify the values inside the array.

The DoubleArrayValues subroutine shown in the following code has a parameter named arr. This parameter is an array of integers and is declared ByVal. The routine loops through the array, doubling each of its values. It then loops through the array, displaying the new values. Next, the subroutine assigns the variable arr to a new array of integers. It loops through the array, again displaying the new values.

Private Sub DoubleArrayValues(ByVal arr() As Integer)
    ' Double the values.
    For i As Integer = arr.GetLowerBound(0) To arr.GetUpperBound(0)
        arr(i) *= 2
    Next i

    ' Display the values.
    For i As Integer = arr.GetLowerBound(0) To arr.GetUpperBound(0)
        Debug.WriteLine(arr(i))
    Next i
    Debug.WriteLine("----------")

    ' Create a new array of values.
    arr = New Integer() {−1, −2}

    ' Display the values.
    For i As Integer = arr.GetLowerBound(0) To arr.GetUpperBound(0)
        Debug.WriteLine(arr(i))
    Next i
    Debug.WriteLine("----------")
End Sub

The following code declares an array of integers containing the values 1, 2, and 3. It invokes the subroutine DoubleArrayValues and then loops through the array, displaying the values after DoubleArrayValues returns.

Dim the_values() As Integer = {1, 2, 3}
DoubleArrayValues(the_values)

For i As Integer = the_values.GetLowerBound(0) To the_values.GetUpperBound(0)
    Debug.WriteLine(the_values(i))
Next i

The following text shows the results. The DoubleArrayValues subroutine lists the array's doubled values 2, 4, 6, assigns a new array to its local variable arr, and then displays the new values −1 and −2. When DoubleArrayValues returns, the main program displays its version of the values. Notice that the values were updated by DoubleArrayValues but that the subroutine's assignment of its arr variable to a new array had no effect on the main program's array the_values.

2
4
6
----------
−1
−2
----------
2
4
6

Now suppose that the subroutine DoubleArrayValues was declared with the following statement:

Private Sub DoubleArrayValues(ByRef arr() As Integer)

In this case, when DoubleArrayValues assigns a new array to its arr variable, the calling routine sees the change, so the the_values array receives the new array. The following text shows the new results:

2
4
6
----------
−1
−2
----------
−1
−2

Parenthesized Parameters

A subroutine can fail to update a parameter declared using the ByRef keyword in a couple ways. The most confusing occurs if you enclose a variable in parentheses when you pass it to the subroutine. Parentheses tell Visual Basic to evaluate their contents as an expression. Visual Basic creates a temporary variable to hold the result of the expression and then passes the temporary variable into the procedure. If the procedure's parameter is declared ByRef, the subroutine updates the temporary variable but not the original variable, so the calling routine doesn't see any change to its value.

The following code calls subroutine DisplayDouble, passing it the variable A surrounded by parentheses. Subroutine DisplayDouble modifies its parameter's value, but the result doesn't get back to the variable A.

Private Sub Main()
    Dim A As Integer = 12
    Debug.WriteLine("Main: " & A)
    DisplayDouble((A))
    Debug.WriteLine("Main: " & A)
End Sub

Private Sub DisplayDouble(ByRef X As Integer)
    X *= 2
    Debug.WriteLine("DisplayDouble: " & X)
End Sub

The following text shows the results:

Main: 12
DisplayDouble: 24
Main: 12

Chapter 15 has more to say about parameters declared with the ByVal and ByRef keywords.

Optional

If you declare a parameter with the Optional keyword, the code that uses it may omit that parameter. When you declare an optional parameter, you must give it a default value for the subroutine to use if the parameter is omitted by the calling routine.

The DisplayError subroutine in the following code takes an optional string parameter. If the calling routine provides this parameter, the subroutine displays it. If the calling routine leaves this parameter out, DisplayError displays its default message "An error occurred." The PlaceOrder subroutine checks its the_customer parameter. If this parameter is Nothing, PlaceOrder calls DisplayError to show the message "Customer is Nothing in subroutine PlaceOrder." Next, subroutine PlaceOrder calls the_customer's IsValid function. If IsValid returns False, the subroutine calls DisplayError. This time it omits the parameter so DisplayError presents its default message.

Private Sub DisplayError(Optional ByVal error_message As String = _
 "An error occurred")
    MessageBox.Show(error_message)
End Sub

Private Sub PlaceOrder(ByVal the_customer As Customer,
 ByVal order_items() As OrderItem)
    ' See if the_customer exists.
    If the_customer Is Nothing Then
        DisplayError("Customer is Nothing in subroutine PlaceOrder")
        Exit Sub
    End If

    ' See if the_customer is valid.
    If Not the_customer.IsValid() Then
        DisplayError()
        Exit Sub
    End If

    ' Generate the order.
    ...
End Sub

Optional parameters must go at the end of the parameter list. If one parameter uses the Optional keyword, all of the following parameters must use it, too.

Optional parameters are particularly useful for initializing values in a class's constructor. The following code shows a DrawableRectangle class. Its constructor takes as parameters the rectangle's position and size. All the parameters are optional, so the main program can omit them if it desires. Because each parameter has default values, the constructor always knows it will have the four values, so it can always initialize the object's Bounds variable.

Public Class DrawableRectangle
    Public Bounds As Rectangle

    Public Sub New(
      Optional ByVal X As Integer = 0,
      Optional ByVal Y As Integer = 0,
      Optional ByVal Width As Integer = 100,
      Optional ByVal Height As Integer = 100
    )
        Bounds = New Rectangle(X, Y, Width, Height)
    End Sub
    ...
End Class

Note that overloaded subroutines cannot differ only in optional parameters. If a call to the subroutine omitted the optional parameters, Visual Basic would be unable to tell which version of the subroutine to use.

Optional versus Overloading

Different developers have varying opinions on whether you should use optional parameters or overloaded routines under various circumstances. For example, suppose that the FireEmployee method could take one or two parameters giving either the employee's name or the name and reason for dismissal. You could make this a subroutine with the reason parameter optional, or you could make one overloaded version of the FireEmployee method for each possible parameter list.

One argument in favor of optional parameters is that overloaded methods might duplicate a lot of code. However, it is easy to make each version of the method call another version that allows more parameters, passing in default values. For example, in the following code the first version of the FireEmployee method simply invokes the second version:

Public Sub FireEmployee(ByVal employee_name As String)
    FireEmployee(employee_name, "Unknown reason")
End Sub

Public Sub FireEmployee(ByVal employee_name As String, ByVal reason As String)
    ...
End Sub

Method overloading is generally superior when the different versions of the routine need to do something different. You might be able to make a single routine with optional parameters take different actions based on the values of its optional parameters, but separating the code into overloaded routines will probably produce a cleaner solution.

Parameter Arrays

Sometimes it is convenient to allow a subroutine to take a variable number of parameters. For example, a subroutine might take as parameters the addresses of people who should receive e-mail. It would loop through the names to send each a message.

One approach is to include a long list of optional parameters. For example, the e-mail subroutine might set the default value for each of its parameters to an empty string. Then it would need to send e-mail to every address parameter that was not empty.

Unfortunately, this type of subroutine would need to include code to deal with each optional parameter separately. This would also place an upper limit on the number of parameters the subroutine can take (however many you are willing to type in the subroutine's parameter list).

A better solution is to use the ParamArray keyword to make the subroutine's final argument a parameter array. A parameter array contains an arbitrary number of parameter values. At runtime, the subroutine can loop through the array to process the parameter values.

The DisplayAverage subroutine shown in the following code takeA sub states a parameter array named values. It checks the array's bounds to make sure it contains at least one value. If the array isn't empty, the subroutine adds the values it contains and divides by the number of values to calculate the average.

' Display the average of a series of values.
Private Sub DisplayAverage(ByVal ParamArray values()As Double)
    ' Do nothing if there are no parameters.
    If values Is Nothing Then Exit Sub
    If values.Length < 1 Then Exit Sub

    ' Calculate the average.
    Dim total As Double = 0
    For i As Integer = LBound(values) To UBound(values)
        total += values(i)
    Next i

    ' Display the result.
    MessageBox.Show((total / values.Length).ToString)
End Sub

The following code shows one way the program could use this subroutine. In this example, DisplayAverage would display the average of the integers 1 through 7, which is 4.

DisplayAverage(1, 2, 3, 4, 5, 6, 7)

Parameter arrays are subject to the following restrictions:

  • A subroutine can have only one parameter array, and it must come last in the parameter list.

  • All other parameters in the parameter list must not be optional.

  • All parameter lists are declared ByVal, so any changes the subroutine makes to the array's contents do not affect the calling routine.

  • Parameter array values are implicitly optional, so the calling routine can provide any number of values (including zero) for the array. However, you cannot use the Optional keyword when you declare the parameter array.

  • All the items in the parameter array must have the same data type. However, you can use an array that contains the generic Object data type and then it can hold just about anything. The downside is you may need to convert the items into a more specific type (for example, using DirectCast or CInt) to use their features.

The calling routine can pass any number of values (including zero) for the parameter array. It can also pass the value Nothing, in which case the subroutine's parameter array has value Nothing.

The program can also pass an array of the appropriate data type in place of the parameter array values. The following two calls to the DisplayAverage subroutine produce the same result inside the DisplayAverage subroutine:

DisplayAverage(1, 2, 3, 4, 5, 6, 7)

Dim values() As Double = {1, 2, 3, 4, 5, 6, 7}
DisplayAverage(values)

Implements interface.subroutine

An interface defines a set of properties, methods, and events that a class implementing the interface must provide. An interface is a lot like a class with all of its properties, methods, and events declared with the MustOverride keyword. Any class that inherits from the base class must provide implementations of those properties, methods, and events.

The IDrawable interface shown in the following code defines a Draw subroutine, a Bounds function, and a property named IsVisible. The DrawableRectangle class begins with the statement Implements IDrawable. That tells Visual Basic that the class will implement the IDrawable interface. If you make the class declaration, type the Implements statement, and then press the Enter key, Visual Basic automatically fills in the declarations you need to satisfy the interface. In this example, it creates the empty Bounds function, Draw subroutine, and IsVisible property procedures shown here. All you need to do is fill in the details.

Public Interface IDrawable
    Sub Draw(ByVal gr As Graphics)
    Function Bounds() As Rectangle
    Property IsVisible() As Boolean
End Interface

Public Class DrawableRectangle
    Implements IDrawable

    Public Function Bounds() As System.Drawing.Rectangle _
     Implements IDrawable.Bounds

    End Function

    Public Sub Draw(ByVal gr As System.Drawing.Graphics) _
     Implements IDrawable.Draw

    End Sub

    Public Property IsVisible() As Boolean Implements IDrawable.IsVisible
        Get

        End Get
        Set(ByVal Value As Boolean)

        End Set
    End Property
End Class

If you look at the preceding code, you can see where the subroutine declaration's Implements interface.subroutine clause comes into play. In this case, the Draw subroutine implements the IDrawable interface's Draw method.

When you type the Implements statement and press the Enter key, Visual Basic generates empty routines to satisfy the interface; then you don't need to type the Implements interface.subroutine clause yourself. Visual Basic enters this for you.

The only time you should need to modify this statement is if you change the interface's name or subroutine name or you want to use some other subroutine to satisfy the interface. For example, you could give the DrawableRectangle class a DrawRectangle method and add Implements IDrawable.Draw to its declaration. Visual Basic doesn't care what you call the routine, as long as some routine implements IDrawable.Draw.

Statements

A subroutine's statements section contains whatever Visual Basic code is needed to get the routine's job done. This can include all the usual variable declarations, For loops, Try blocks, and other Visual Basic paraphernalia.

The subroutine's body cannot include module, class, subroutine, function, structure, enumerated type, or other file-level statements. For example, you cannot define a subroutine within another subroutine.

One new statement that you can use within a subroutine is Exit Sub. This command makes the subroutine immediately exit and return control to the calling routine. Within a subroutine, the Return statement is equivalent to Exit Sub.

You can use Exit Sub or Return as many times as you like to allow the subroutine to exit under different conditions. For example, the following subroutine checks whether a phone number has a 10-digit or 7-digit format. If the phone number matches a 10-digit format, the subroutine exits. Then if the phone number matches a 7-digit format, the subroutine exits. If the number doesn't match either format, the subroutine displays an error message to the user.

Private Sub ValidatePhoneNumber(ByVal phone_number As String)
    ' Check for a 10-digit phone number.
    If phone_number Like "###-###-####" Then Exit Sub

    ' Check for a 7-digit phone number.
    If phone_number Like "###-####" Then Return

    ' The phone number is invalid.
    MessageBox.Show("Invalid phone number " & phone_number)
End Sub

FUNCTIONS

Functions are basically the same as subroutines, except that they return some sort of value. The syntax for defining a function is as follows:

[attribute_list] [inheritance_mode] [accessibility] _
Function function_name([parameters]) [As return_type] [ Implements interface.function ]
    [ statements ]
End function

This is almost the same as the syntax for defining a subroutine. See the section, "Subroutines," earlier in this chapter for information about most of this declaration's clauses.

One difference is that a function ends with the End Function statement rather than End Sub. Similarly, a function can exit before reaching its end using Exit Function rather than Exit Sub.

The one nontrivial difference between subroutine and function declarations is the clause As return_type that comes after the function's parameter list. This tells Visual Basic the type of value that the function returns.

The function can set its return value in one of two ways. First, it can set its name equal to the value it wants to return. The Factorial function shown in the following code calculates the factorial of a number. Written N!, the factorial of N is N * (N21) * (N22) . . .* 1. The function initializes its result variable to 1, and then loops over the values between 1 and the number parameter, multiplying these values to the result. It finishes by setting its name, Factorial, equal to the result value that it should return.

Private Function Factorial(ByVal number As Integer) As Double
    Dim result As Double = 1

    For i As Integer = 2 To number
        result *= i
    Next i

    Factorial = result
End function

A function can assign and reassign its return value as many times as it wants to before it returns. Whatever value is assigned last becomes the function's return value.

The second way a function can assign its return value is to use the Return keyword followed by the value that the function should return. The following code shows the Factorial function rewritten to use the Return statement:

Private Function Factorial(ByVal number As Integer) As Double
    Dim result As Double = 1

    For i As Integer = 2 To number
        result *= i
    Next i

    Return result
End function

The Return statement is roughly equivalent to setting the function's name equal to the return value, and then immediately using an Exit Function statement. The Return statement may allow the compiler to perform extra optimizations, however, so it is generally preferred to setting the function's name equal to the return value. (Return is also the more modern syntax and has become so common that some developers don't even recognize the other syntax anymore.)

PROPERTY PROCEDURES

Property procedures are routines that can represent a property value for a class. The simplest kind of property is an auto-implemented property. Simply add the Property keyword to a variable declaration as shown in the following code:

Public Property LastName As String

If you want, you can give the property a default value as in the following code:

Public Property LastName As String = "<missing>"

Behind the scenes, Visual Basic makes a hidden variable to hold the property's value. When other parts of the program get or set the value, Visual Basic uses the hidden variable.

This type of property is easy to make but it has few advantages over a simple variable. You can make the property more powerful if you write your own procedures to get and set the property's value. If you write your own procedures you can add validation code, perform complex calculations, save and restore values in a database, set breakpoints, and add other extras to the property.

A normal read-write property procedure contains a function for returning the property's value and a subroutine for assigning it.

The following code shows property procedures that implement a Value property. The Property Get procedure is a function that returns the value in the private variable m_Value. The Property Set subroutine saves a new value in the m_Value variable.

Private m_Value As Single

Property Value() As Single
    Get
        Return m_Value
    End Get
    Set(ByVal Value As Single)
        m_Value = Value
    End Set
End Property

Although the property is implemented as a pair of property procedures, the program could treat the value as a simple property. For example, suppose that the OrderItem class contains the preceding code. Then the following code sets the Value property for the OrderItem object named paper_item:

paper_item.Value = 19.95

You can add property procedures to any type of object module. For example, you can use property procedures to implement a property for a form or for a class of your own.

It's less obvious that you can also use property procedures in a code module. The property procedures look like an ordinary variable to the routines that use them. If you place the previous example in a code module, the program could act as if there were a variable named Value defined in the module.

For more information on property procedures, see the section "Property Procedures" in Chapter 15.

EXTENSION METHODS

Extension methods allow you to add a new method to an existing class without rewriting it or deriving a new class from it. To make an extension method, decorate the method declaration with the Extension attribute. Then make a normal subroutine or function that takes one or more parameters. The first parameter determines the class that the method extends. The method can use that parameter to learn about the item for which the method was called. The other parameters are passed into the method so it can use them to perform its chores.

For example, the following code adds a MatchesRegexp subroutine to the String class. The Extension attribute tells Visual Basic that this is an extension method. The method's first parameter is a String so this method extends the String class. The second parameter is a regular expression. The method returns True if the String matches the regular expression.

' Return True if a String matches a regular expression.
<Extension()>
Public Function MatchesRegexp(ByVal the_string As String,
 ByVal regular_expression As String) As Boolean
    Dim reg_exp As New Regex(regular_expression)
    Return reg_exp.IsMatch(the_string)
End function

The following code shows how a program might use this method to decide whether the string stored in variable phone_number looks like a valid 7-digit United States phone number:

if Not phone_number.MatchesRegexp("^[2-9]d{2}-d{4}$") Then
    MessageBox.Show("Not a valid phone number")
End if

Example program ValidatePhone demonstrates the MatchesRegexp extension method. It also uses the MatchesRegexp method to define the following three additional extension methods that determine whether a string looks like a valid 7- or 10-digit United States phone number. These methods simply call the MatchesRegexp method, passing it appropriate regular expressions.

' Return True if a String looks like a 7-digit US phone number.
<Extension()>
Public Function IsValidPhoneNumber7digit(ByVal the_string As String) As Boolean
    Return the_string.MatchesRegexp("^[2-9]d{2}-d{4}$")
End Function

' Return True if a String looks like a 10-digit US phone number.
<Extension()>
Public Function IsValidPhoneNumber10digit(ByVal the_string As String) As Boolean
    Return the_string.MatchesRegexp("^([2-9]d{2}-){2}d{4}$")
End Function

' Return True if a String looks like a 7- or 10-digit US phone number.
<Extension()>
Public Function IsValidPhoneNumberUS(ByVal the_string As String) As Boolean
    Return IsValidPhoneNumber7digit(the_string) OrElse
           IsValidPhoneNumber10digit(the_string)
End function
                                                  
EASIER EXTENSIONS

If you build a class and later need to change its features, it's usually easiest to modify its code directly. That will cause less confusion than extension methods, which may lie in some obscure module that seems unrelated to the original class. If you need to add methods to existing classes that you cannot modify directly, such as String and other classes defined by Visual Basic and the .NET Framework, extension methods can be extremely useful.

LAMBDA FUNCTIONS

Lambda functions are functions that are defined within the flow of the program's code. Often they are defined, used, and forgotten in a single statement without ever being given a name.

To define a lambda function for later use, start with the Function keyword. Add the function's name and any parameters that it requires, followed by a single statement that evaluates to the value that the function should return.

Next include either (1) a single statement that evaluates to the value that the function should return, or (2) a function body that ends with an End Function statement.

The following code fragment shows examples of both of these styles. First the program creates a lambda function named square_it that takes parameter n and returns n * n. It then creates a multiline lambda function named factorial that calculates and returns a number's factorial. The code finishes by calling both functions and displaying their results.

Dim square_it = Function(n As Integer) n * n
Dim factorial = Function(n As Integer) As Integer
                    Dim result As Integer = 1
                    For i As Integer = 2 To n
                        result *= i
                    Next i
                    Return result
                End Function

Debug.WriteLine(square_it(5))
Debug.WriteLine(factorial(5))

Example program LambdaFunction contains the following code fragment:

' Define a lambda function that adds two integers.
Dim plus = Function(i1 As Integer, i2 As Integer) i1 + i2

' Get A and B.
Dim A As Integer = Integer.Parse(txtA.Text)
Dim B As Integer = Integer.Parse(txtB.Text)

' Call the lambda function to calculate the result.
txtResult.Text = plus(A, B).ToString
                                                  
LAMBDA FUNCTIONS

This code starts by defining a variable named plus. This variable holds a reference to a lambda function that takes two integers as parameters and returns their sum. The code then gets input values from text boxes and calls the plus function, passing it those values. It converts the result into a string and displays it in the txtResult text box.

This example creates a variable to hold a reference to a lambda function and then invokes the function by using that variable. It could just as easily have invoked the lambda function itself while defining it.

Example program InlineFunction, which is also available for download on the book's web site, demonstrates this in the following line of code. This line defines the function and invokes it without ever saving a reference to it.

txtResult.Text =
    (Function(i1 As Integer, i2 As Integer) i1 + i2)(A, B).ToString

Because lambda functions are declared in a single line of code, they are also called inline functions. A lambda function defined inside a subroutine or function is also sometimes called a nested function.

No matter which method the program uses to define a lambda function, it could then pass the function to another routine that will later call the function. For example, suppose subroutine PerformCalculations takes as a parameter the function it should use to perform its calculations. The following code shows how a program could call subroutine PerformCalculations while passing it the previous lambda functions:

' Define the plus function.
Dim plus = Function(i1 As Integer, i2 As Integer) i1 + i2

' Call PerformCalculations passing it the lambda function.
PerformCalculations(plus)

' Call PerformCalculations passing it an inline lambda function.
PerformCalculations(Function(i1 As Integer, i2 As Integer) i1 + i2)

Inline functions were invented for use by LINQ and are most often used with LINQ. For more information about LINQ, see Chapter 21, "LINQ."

Visual Basic 2010 also adds the ability to write lambda subroutines that are similar to lambda functions except they don't return a value.

The following code defines two named lambda subroutines. The first does all of its work on a single line whereas the second uses the multiline format. After defining the subroutines, the code invokes them to display two messages.

Dim write_msg = Sub(ByRef msg As String) Debug.WriteLine("write_msg: " & msg)
Dim show_msg = Sub(ByRef msg As String)
                   MessageBox.Show("show_msg: " & msg)
               End Sub

write_msg("Hi")
show_msg("Hi again")

As with lambda functions, you can build and pass a lambda subroutine into another routine as a parameter as shown in the following code:

Delegate Sub MsgFunc(ByVal m As String)

Private Sub DisplayMessage(ByVal msg As String, ByVal msg_func As MsgFunc)
    msg_func(msg)
End Sub

Private Sub TestSub()
    DisplayMessage("Hello?", Sub(m As String) MessageBox.Show(m))
End Sub

The code first declares a delegate type named MsgFunc that represents a subroutine that takes a String parameter.

Subroutine DisplayMessage takes as parameters a String and a subroutine of type MsgFunc. It calls the subroutine, passing it the String parameter.

The test subroutine TestSub calls DisplayMessage passing it a String and a lambda subroutine created inline.

RELAXED DELEGATES

If you assign a variable to the value in a variable of a different type, Visual Basic automatically converts the value into the correct type under some circumstances. If you set a Single variable equal to an Integer variable, Visual Basic automatically converts the Integer into a Single.

If Option Strict is off, you can also do the reverse: if you assign an Integer variable equal to a Single variable, Visual Basic converts the Single into an Integer (if it can).

In a similar manner, relaxed delegates let Visual Basic convert method parameters from one data type to another under certain circumstances. If the code invokes a subroutine by using a delegate, Visual Basic tries to convert parameters when it can. Probably the easiest way to understand how this works is to consider an example.

The following code declares a delegate type named TestDelegate. Methods that match this delegate should be subroutines that take a Control as a parameter.

' Declare the delegate type.
Private Delegate Sub TestDelegate(ByVal ctl As Control)

The following code defines three subroutines that take parameters of different types. The first takes an Object as a parameter, the second takes a TextBox, and the third takes no parameters. Note that the first subroutine cannot work if Option Strict is on. Option Strict disallows late binding, so the code cannot use a Text property provided by a generic Object.

' A more general parameter type.
Private Sub Test1(ByVal obj As Object)
    obj.Text = "Test1" ' Needs Option Strict off.
End Sub

' A more specific parameter type.
Private Sub Test2(ByVal text_box As TextBox)
    text_box.Text = "Test2"
End Sub

' Parameter omitted.
Private Sub Test3()
    txtField3.Text = "Test3"
End Sub

The following code declares three variables of the TestDelegate type and sets them equal to the addresses of the three test subroutines:

' Make variables of the delegate type
' hold references to the subroutines.
Private Sub1 As TestDelegate = AddressOf Test1
Private Sub2 As TestDelegate = AddressOf Test2 ' Needs Option Strict off.
Private Sub3 As TestDelegate = AddressOf Test3

The first assignment works even though subroutine Test1 does not exactly match the delegate type. Subroutine Test1 takes an Object as a parameter and TestDelegate takes a Control as a parameter. When Visual Basic invokes the Sub1 variable, it will pass the subroutine a Control object as a parameter because Sub1 has type TestDelegate, and that type takes a Control as a parameter. A Control is a type of Object, so Visual Basic can safely pass a Control in place of an Object parameter. That allows the code assigning Sub1 to the address of subroutine Test1 to work.

The second line of code that assigns variable Sub2 to subroutine Test2 works only if Option Strict is off. When Visual Basic invokes the Sub2 variable, it will pass the subroutine a Control object as a parameter because Sub1 has type TestDelegate, and that type takes a Control as a parameter. Subroutine Test2 takes a TextBox as a parameter, and not every Control is a TextBox. That means at design time Visual Basic cannot tell whether it can safely invoke the Sub2 delegate so, if Option Strict is on, Visual Basic flags this assignment as an error. If Option Strict is off, Visual Basic allows the assignment, although the program will crash if it tries to pass a control that is not a TextBox into Sub2 at runtime.

The final assignment sets variable Sub3 to the address of subroutine Test3. Subroutine Test3 takes no parameters. This is a special case that Visual Basic allows: if the method does not need to use the parameters specified by the delegate, it can omit its parameters. Note that the method must omit all or none of the parameters; it cannot omit some and not others.

The following code invokes the subroutines pointed to by the three TestDelegate variables, passing each a reference to a different TextBox. Sub1 treats txtField1 as an Object, Sub2 treats txtField2 as a TextBox, and Sub3 ignores its parameter completely.

Sub1(txtField1)
Sub2(txtField2)
Sub3(txtField3)
' Test3(txtField3) ' This doesn't work.

The final line of code, that invokes subroutine Test3 directly, doesn't work. Omitting the parameter list from a method only works if you access the method from a delegate. If you call the method directly, the parameter list must match the one declared for the method.

Example program RelaxedDelegates, which is available for download on the book's web site, demonstrates this code.

All of these relaxed delegate rules are somewhat confusing. They give you a little more flexibility, but they can make the code a lot more confusing. You may wonder why you should bother. In fact, if you use delegates such as those shown in this example, you might want to avoid using relaxed delegates to keep the code easier to understand.

These rules also apply to event handlers, and in that context they are fairly useful. They let you change an event handler's parameter types to make them more general or more specific, or to omit them.

The following code shows a simple, standard Button Click event handler. It takes two parameters of types Object and EventArgs. In this example, the code reads a text file into a text box.

Private Sub btnLoad_Click(ByVal sender As System.Object,
 ByVal e As System.EventArgs) Handles btnLoad.Click
    txtContents.Text = System.IO.File.ReadAllText(txtFile.Text)
End Sub

Many event handlers must deal explicitly with the control that raised their event. In that case, the first thing the event handler usually does is convert the generic sender parameter from an Object into a more specific control type.

The following code defines a Button Click event handler similar to the previous one but this one declares its sender parameter to be of type Button. This works as long as the event is actually raised by a Button, so the sender parameter really is a button. If you were to attach this event handler to a TextBox's TextChanged event, the program would crash when Visual Basic tries to convert the TextBox into a Button when it raises the event.

' Needs Option Strict off.
Private Sub btnLoad2_Click(ByVal btn As Button,
 ByVal e As Object) Handles btnLoad2.Click
    txtContents.Text = System.IO.File.ReadAllText(txtFile.Text)
End Sub

Note that this version requires Option Strict off. If Option Strict is on, Visual Basic will not allow this subroutine to handle a Button's Click event. This is similar to the way Option Strict prevents you from setting a Button variable equal to a generic Object variable.

The previous code declares its parameters to have a more restrictive type than those passed into it by the control raising the event. You can also make the parameters more general. You could declare the e parameter to be of type Object instead of EventArgs. Usually, that doesn't help you much. It could be useful if you want to use the same event handler to catch different kinds of events that provide different types of arguments, but it's hard to imagine a really good example where that wouldn't be confusing.

A more common situation is where the event handler ignores its parameters completely. Usually each Button has its own Click event handler so you don't need to look at the parameters to figure out which button was clicked.

The following code defines a Button Click event handler that takes no parameters. When the user clicks the btnLoad3 Button, Visual Basic doesn't pass the event handler any parameters. This code is easier to read than the previous versions, partly because the Sub statement fits all on one line.

Private Sub btnLoad3_Click() Handles btnLoad3.Click
    txtContents.Text = System.IO.File.ReadAllText(txtFile.Text)
End Sub

Example program RelaxedEventHandlers, which is available for download on the book's web site, demonstrates relaxed event handlers.

Relaxed delegates may add more confusion than they're worth if you use delegate variables, but they can be useful for simplifying event handlers. Declaring parameters with a more specific type (for example, Button instead of Object) can make the code easier to write and understand, although it has the large drawback of requiring Option Strict off. Omitting parameters when you don't need them is an even better technique. It simplifies the code without forcing you to turn Option Strict off.

PARTIAL METHODS

A partial method is a private subroutine that is declared in one place and implemented in another. The code includes a subroutine declaration that uses the Partial keyword and that has an empty body. Another part of the class declares the method again, this time without the Partial keyword and providing a method body.

What's the point? Partial methods were invented for the convenience of code generators. The details are somewhat technical and not relevant for developers at this point so they are only briefly considered here.

Partial methods were designed as a more efficient alternative to events. Rather than raising an event for a class to catch, the generated code can call a partial method. If the method has no body, the compiler optimizes the call away and nothing happens, much as would be the case if an object did not catch an event.

The following list summarizes the differences between event handlers and partial methods:

  • An event can be caught by any number of event handlers, but a partial method has only one body.

  • An event can be declared Public, Private, or Friend, but a partial method must be Private.

  • Raising an event requires some overhead even if no event handlers catch it. If a partial method has no body, the compiler ignores calls to it so there's no overhead.

  • A program can add and remove event handlers at runtime, but a partial method is given a body or not at design time.

  • Just about any piece of code can catch an object's events, but only that object can see its partial method (because it's private).

Partial methods are really intended for use by code generators, but it's conceivable that you might find a use for them. It's also worth knowing about them so you know what's going on if you see them in automatically generated code.

Example program PartialMethods uses the following code to define the TestMethod subroutine:

Public Class Form1
    ' Define the TestMethod subroutine without a method body.
    Partial Private Sub TestMethod(ByVal msg As String)

    End Sub

    ' Other code omitted ...
End Class
                                                  
PARTIAL METHODS

The example uses the following code in a separate module to define the method's body:

Partial Public Class Form1
    ' Define the implementation for TestMethod.
    Private Sub TestMethod(ByVal msg As String)
        MessageBox.Show(msg)
    End Sub
End Class
                                                  
PARTIAL METHODS

When you click the program's button, the code calls subroutine TestMethod, passing it a string to display. If you comment out the method's body definition, the program ignores this call.

You can achieve results similar to methods without using partial methods in a couple of ways. First, you can make a class raise an event. If no code catches the event, the event is essentially ignored much as a partial method call is ignored if you have not defined the method body.

A second approach is to decorate a method with the Conditional attribute. In that case, Visual Basic removes the method and any calls to it from the code if the condition is not satisfied. The AttributeConditional example program, which is available for download on the book's web site, demonstrates this approach. For more information about that example, see the section "Attribute_List" earlier in this chapter.

Partial methods are also somewhat similar to interfaces, which also define a method's signature but don't provide an implementation.

Finally, partial methods are similar to classes with overridable methods. Any derived classes can override the overridable methods to give them method bodies. If the parent class gives the method a body, the child class can leave it alone and inherit the parent's version as a default.

Partial methods are really intended for use by code generators, but you can use them if you wish.

SUMMARY

Subroutines and functions let you break an application into manageable, reusable pieces. A subroutine performs a series of commands. A function performs a series of commands and returns a value.

Property procedures use paired functions and subroutines to provide the behavior of a simple property using routines.

These form the fundamental building blocks of the procedural part of an application. Chapters 25 through 29 explain the other half of an application's structure: the objects that encapsulate the application's behavior. Together, the program's objects and its procedural subroutines and functions define the application.

This chapter explains how to break an otherwise unwieldy expanse of code into subroutines and functions of manageable size. It also explains techniques related to subroutines and functions, such as extension methods and relaxed delegates, that let you use existing classes and events in new ways.

The chapters so far do not explain how to write anything other than straight-line code that executes one statement after another with no deviation. Most programs need to follow more complex paths of execution, performing some statements only under certain conditions and repeating others a given number of times. Chapter 18, "Program Control Statements," describes the statements that a Visual Basic program uses to control the flow of code execution. These include decision statements (If Then Else, Select Case, IIF, Choose) and looping statements (For Next, For Each, Do While, While Do, Repeat Until).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.214.32