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.


DELIGHTFUL DECORATIONS
Applying an attribute to a class, variable, method, or other code entity is sometimes called decorating the entity.

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

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, so only a few of the most useful are described here. 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 useful only in very specialized circumstances, so it isn’t described here.

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 or a PropertyGrid control. If you pass the attribute’s constructor the value False, the Properties window and PropertyGrid controls do 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 or a PropertyGrid control. 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 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 the Form Designer, 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.
  • 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 13, “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.
  • 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. 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 box 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 run time. 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 debugger skips over the method and will not stop at breakpoints inside it.
  • DebuggerStepThrough — This tells debuggers whether to let the developer step into a method in the debugger. If the DebuggerStepThrough attribute is present, the IDE will not step into the method.
  • 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. Attributes in the System.Xml.Serialization namespace can provide a lot of control over serializations.

FINDING ATTRIBUTES
Finding the attributes that are useful for a particular task can be tricky. It helps to realize that attribute classes inherit either directly or indirectly from the Attribute class. You can get information about the Attribute class at http://msdn2.microsoft.com/system.attribute.aspx. You can see a list of classes that inherit from System.Attribute at http://msdn2.microsoft.com/2e39z096.aspx.

Inheritance_Mode

In a routine’s declaration, 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(X As Integer, 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 create an instance of the class and try to 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 should invoke the subroutine by using the class’s name (ClassName.SharedSub) instead of an 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 and control libraries where some other project may use the library.
  • 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 numbers and types of the parameters that the subroutine takes as arguments. This section also gives the names the subroutine will use to refer to the values.

Declaring parameters is very similar to declaring variables. See Chapter 14, “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 argument. The subroutine can modify this value all it wants and the corresponding value in the calling procedure isn’t changed.

ByRef

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

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 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.

If the array is declared ByRef, the subroutine can also change the memory to which the array points. It can set the parameter to a completely new array and the calling code will see the change in the array that it passed to the subroutine.

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 14 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 demonstrates an optional string parameter:

Private Sub DisplayError(Optional error_message As String =
 "An error occurred")
    MessageBox.Show(error_message)
End Sub
 
Private Sub PlaceOrder(the_customer As Customer, 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

If the calling routine provides the optional error_message parameter, the subroutine displays it. If the calling routine leaves this parameter out, DisplayError uses 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 without the parameter so DisplayError presents its default message.

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


OPTIONAL AND NULLABLE
Nullable parameters can also be optional. For example, the following code defines three subroutines that each take an optional nullable parameter. The first two give the parameter the default value Nothing, and the third uses the default value 0.
Public Sub Sub1(Optional ByVal x? As Integer = Nothing)
    ...
End Sub
 
Public Sub Sub2(Optional ByVal x As Integer? = Nothing)
    ...
End Sub
 
Public Sub Sub3(Optional ByVal x As Nullable(Of Integer) = 0)
    ...
End Sub

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 X As Integer = 0,
      Optional Y As Integer = 0,
      Optional Width As Integer = 100,
      Optional 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(employee_name As String)
    FireEmployee(employee_name, "Unknown reason")
End Sub
 
Public Sub FireEmployee(employee_name As String, 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 run time, the subroutine can loop through the array to process the parameter values.

The DisplayAverage subroutine shown in the following code takes a parameter array named values. It first 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(ParamArray values() As Double)
    ' Do nothing if there are no parameters.
    If values Is Nothing Then Exit Sub
    If values.Length = 0 Then Exit Sub
 
    ' Display the average.
    MessageBox.Show((values.Sum()/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 must be declared ByVal.
  • The calling code may pass the value Nothing in the place of the parameter array. (That’s why the code in the previous example checked whether values was Nothing before continuing.)
  • The calling code can provide any number of values for the parameter array including zero. (That’s why the code in the previous example checked whether values.Length was zero before continuing.)
  • Even though the values passed into a parameter array are in a sense optional, 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 program can also pass an array of the appropriate data type in place of a series of 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.


NAMING CONVENTION
Developers often begin the name of interfaces with a capital I so that it’s obvious that it’s an interface. In fact, it’s such a common practice and has no disadvantages that it should practically be a requirement. Start interface names with “I” so other developers know they are interfaces.

The following code defines the IDrawable interface and the IDrawableRectangle that implements it:

Public Interface IDrawable
    Sub Draw(gr As Graphics)
    Function Bounds() As Rectangle
    Property IsVisible As Boolean
End Interface
 
Public Class DrawableRectangle
    Implements IDrawable
 
    Public Function Bounds() As Rectangle Implements IDrawable.Bounds
 
    End Function
 
    Public Sub Draw(gr As Graphics) Implements IDrawable.Draw
 
    End Sub
 
    Public Property IsVisible As Boolean Implements IDrawable.IsVisible
End Class

The IDrawable interface 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.

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, If Then statements, 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 statement that I haven’t mentioned before 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. Finally if the number doesn’t match either format, the subroutine displays an error message to the user.

Private Sub ValidatePhoneNumber(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
..................Content has been hidden....................

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