Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
(ebook) Visual Studio .NET Mastering Visual Basic.pdf
Скачиваний:
120
Добавлен:
17.08.2013
Размер:
15.38 Mб
Скачать

170 Chapter 4 WRITING AND USING PROCEDURES

The arguments to similar procedures are usually of equal stature, and their order doesn’t make any difference. A function that calculates the mean or other basic statistics of a set of numbers, or a subroutine that populates a ListBox or ComboBox control, are prime candidates for implementing using this technique. If the procedure accepts a variable number of arguments that aren’t equal in stature, then you should consider the technique described in the following section.

Named Arguments

You’ve learned how to write procedures with optional arguments and how to pass a variable number of arguments to the procedure. The main limitation of the argument-passing mechanism, though, is the order of the arguments. If the first argument is a string and the second is a date, you can’t change their order. By default, Visual Basic matches the values passed to a procedure to the declared arguments by their order. That’s why the arguments you’ve seen so far are called positional arguments.

This limitation is lifted by Visual Basic’s capability to specify named arguments. With named arguments, you can supply arguments in any order, because they are recognized by name and not by their order in the list of the procedure’s arguments. Suppose you’ve written a function that expects three arguments: a name, an address, and an e-mail address:

Function Contact(Name As String, Address As String, EMail As String)

When calling this function, you must supply three strings that correspond to the arguments Name, Address, and EMail, in that order. However, there’s a safer way to call this function: supply the arguments in any order by their names. Instead of calling the Contact function as follows:

Contact(“Peter Evans”, “2020 Palm Ave., Santa Barbara, CA 90000”, _

“PeterEvans@example.com”)

you can call it this way:

Contact(Address:=”2020 Palm Ave., Santa Barbara, CA 90000”, _

EMail:=”PeterEvans@example.com”, Name:=”Peter Evans”)

The := operator assigns values to the named arguments. Because the arguments are passed by name, you can supply them in any order.

To test this technique, enter the following function declaration in a form’s code:

Function Contact(ByVal Name As String, ByVal Address As String, _

ByVal EMail As String) As String

Console.WriteLine(Name)

Console.WriteLine(Address)

Console.WriteLine(EMail)

Return (“OK”)

End Function

Then, call the Contact() function from within a button’s Click event with the following statement:

Console.WriteLine(Contact(Address:=”2020 Palm Ave., Santa Barbara, CA 90000”, _ Name:=”Peter Evans”, EMail:=”PeterEvans@example.com”))

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

ARGUMENTS 171

You’ll see the following in the Immediate window:

Peter Evans

2020 Palm Ave., Santa Barbara, CA 90000

PeterEvans@example.com

OK

The function knows which value corresponds to which argument and can process them the same way that it processes positional arguments. Notice that the function’s definition is the same whether you call it with positional or named arguments. The difference is in how you call the function and how you declare it.

Named arguments make code safer and easier to read, but because they require a lot of typing, most programmers don’t use them. Besides, programmers are so used to positional arguments that the notion of naming arguments is like having to declare variables when variants will do. Named arguments are good for situations in which you have optional arguments that require many consecutive commas, which may complicate the code. The methods of the various objects exposed by the Office applications (discussed in Chapter 10) require a large number of arguments, and they’re frequently called with named arguments.

More Types of Function Return Values

Functions are not limited to returning simple data types like integers or strings. They may return custom data types and even arrays. The ability of functions to return all types of data makes them very flexible and can simplify coding, so we’ll explore it in detail in the following sections. Using complex data types, such as structures and arrays, allows you to write functions that return multiple values.

Functions Returning Structures

Suppose you need a function that returns a customer’s savings and checking balances. So far, you’ve learned that you can return two or more values from a function by supplying arguments with the ByRef keyword. A more elegant method is to create a custom data type (a structure) and write a function that returns a variable of this type. The structure for storing balances could be declared as follows:

Structure CustBalance

Dim BalSavings As Decimal

Dim BalChecking As Decimal

End Structure

Then, you can define a function that returns a CustBalance data type as:

Function GetCustBalance(ByVal custID As Integer) As CustBalance { statements }

End Function

The GetCustBalance() function must be defined in the same module as the declaration of the custom data type it returns. If not, you’ll get an error message.

When you call this function, you must assign its result to a variable of the same type. First declare a variable of the CustBalance type and then use it as shown here:

Private Balance As CustBalance

Dim custID As Integer

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

172 Chapter 4 WRITING AND USING PROCEDURES

custID = 13011

Balance = GetCustBalance(custID) Console.WriteLine(Balance.BalSavings) Console.WriteLine(Balance.BalChecking)

Here, custID is a customer’s ID (a number or string, depending on the application). Of course, the function’s body must assign the proper values to the CustBalance variable’s fields.

Here’s the simplest example of a function that returns a custom data type. This example outlines the steps you must repeat every time you want to create functions that return custom data types:

1.Create a new project and insert the declarations of a custom data type in the declarations section of the form:

Structure CustBalance

Dim BalSavings As Decimal

Dim BalChecking As Decimal

End Structure

2.Then implement the function that returns a value of the custom type. You must declare a variable of the type returned by the function and assign the proper values to its fields. The following function assigns random values to the fields BalChecking and BalSavings. Then, assign the variable to the function’s name, as shown next:

Function GetCustBalance(ID As Long) As CustBalance Dim tBalance As CustBalance tBalance.BalChecking = CDec(1000 + 4000 * rnd()) tBalance.BalSavings = CDec(1000 + 15000 * rnd()) GetCustBalance = tBalance

End Function

3.Then place a button on the form from which you want to call the function. Declare a variable of the same type and assign to it the function’s return value. The example that follows prints the savings and checking balances on the Output window:

Private Sub Button1_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles Button1.Click Dim balance As CustBalance

balance = GetCustBalance(1) Console.WriteLine(balance.BalChecking) Console.WriteLine(balance.BalSavings)

End Sub

For this example, I created a project with a single form. The form contains a single Command button whose Click event handler is shown here. Create this project from scratch, perhaps using your own custom data type, to explore its structure and experiment with functions that return custom data types.

In the following section, I’ll describe a more complicated (and practical) example of a custom data type function.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

ARGUMENTS 173

VB.NET at Work: The Types Project

The Types project, which you’ll find in this chapter’s folder on the CD, demonstrates a function that returns a custom data type. The Types project consists of a form that displays record fields and is shown in Figure 4.2. Every time you click the View Next button, the fields of the next record are displayed. When all records are exhausted, the program wraps back to the first record.

Figure 4.2

The Types project demonstrates functions that return custom data types.

The project consists of a single form. The following custom data type appears in the form’s code, outside any procedure:

Structure Customer

Dim Company As String

Dim Manager As String

Dim Address As String

Dim City As String

Dim Country As String

Dim CustomerSince As Date

Dim Balance As Decimal

End Structure

Private Customers(8) As Customer

Private cust As Customer

Private currentIndex as Integer

The array Customers holds the data for nine customers, and the cust variable is used as a temporary variable for storing the current customer’s data. The currentIndex variable is the index of the current element of the array.

The Click event handler of the View Next button calls the GetCustomer() function with an index value (which is the order of the current customer), and displays its fields in the Label controls on the form. Then it increases the value of the currentIndex variable, so that it points to the next customer.

The GetCustomer() function returns a variable of Customer type (the variable aCustomer). The code behind the View Next button follows:

Private Sub Button1_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles Button1.Click If currentIndex = CountCustomers() Then currentIndex = 0

Dim aCustomer As Customer

aCustomer = GetCustomer(currentIndex) ShowCustomer(currentIndex) currentIndex = currentIndex + 1

End Sub

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

174 Chapter 4 WRITING AND USING PROCEDURES

The CountCustomers() function returns the number of records stored in the Customers array. The event handler starts by comparing the value of the current index to the number of elements in the Customers array. If they’re equal, the currentIndex variable is reset to zero. The definitions of the CountCustomers() and GetCustomer() functions are shown next:

Function CountCustomers() As Integer

Return(Customers.Length)

End Function

Function GetCustomer(ByVal idx As Integer) As Customer

Return(Customers(idx))

End Function

Finally, the ShowCustomer() subroutine displays the fields of the current record on the Label controls on the form:

Sub ShowCustomer(ByVal idx As Integer) Dim aCustomer As Customer aCustomer = GetCustomer(idx) lblCompany.Text = aCustomer.Company

lblSince.Text = aCustomer.CustomerSince lblAddress.Text = aCustomer.Address lblCity.Text = aCustomer.City lblCountry.Text = aCustomer.Country lblBalance.Text = aCustomer.Balance

End Sub

The array Customers is populated when the program starts with a call to the InitData() subroutine (also in the project’s module). The program assigns data to Customers, one element at a time, with statements like the following:

Dim cust As Customer

cust.Company = “Bottom-Dollar Markets” cust.Manager = “Elizabeth Lincoln” cust.Address = “23 Tsawassen Blvd.” cust.City = “Tsawassen”

cust.Country = “Canada” cust.CustomerSince = #10/20/1996# cust.Balance = 33500 Customers(1) = cust

The code assigns values to the fields of the cust variable and then assigns the entire variable to an element of the Customers array. The data could originate in a file or even a database. This wouldn’t affect the operation of the application, which expects the GetCustomer() function to return a record of Customer type. If you decide to store the records in a file or a collection like the ones discussed in Chapter 11, the form’s code need not change; only the implementation of the GetCustomer() function will change. You should also change the CountCustomers() function, so that it detects when it has reached the last record.

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

ARGUMENTS 175

The Types project uses a single button that allows users to view the next record. You can place another button that displays the previous record. This button’s code will be identical to the code of the existing button, with the exception that it will decrease the currentIndex variable.

Functions Returning Arrays

In addition to returning custom data types, VB.NET functions can also return arrays. This is an interesting possibility that allows you to write functions that return not only multiple values, but also an unknown number of values. Earlier in the chapter you saw how to return multiple values from a function as arguments, passed to the function by reference. You can also consider a custom structure as a collection of values. In this section, we’ll revise the Stats() function that was described earlier in this chapter, so that it returns the statistics in an array. The new Stats() function will return not only the average and the standard deviation, but the minimum and maximum values in the data set as well. One way to declare a function that calculates all the statistics is the following:

Function Stats(ByRef DataArray() As Double) As Double()

This function accepts an array with the data values and returns an array of doubles. This notation is more compact and helps you write easier-to-read code.

To implement a function that returns an array, you must do the following:

1.Specify a type for the function’s return value, and add a pair of parentheses after the type’s name. Don’t specify the dimensions of the array to be returned here; the array will be declared formally in the function.

2.In the function’s code, declare an array of the same type and specify its dimensions. If the function should return four values, use a declaration like this one:

Dim Results(3) As Double

The Results array will be used to store the results and must be of the same type as the func- tion—its name can be anything.

3. To return the Results array, simply use it as argument to the Return statement:

Return(Results)

4. In the calling procedure, you must declare an array of the same type without dimensions:

Dim Stats() As Double

5. Finally, you must call the function and assign its return value to this array:

Stats() = Stats(DataSet())

Here, DataSet is an array with the values whose basic statistics will be calculated by the Stats() function. Your code can then retrieve each element of the array with an index value as usual.

VB.NET at Work: The Statistics Project

The next project demonstrates how to design and call functions that return arrays. It’s the Statistics project, which you can find in this chapter’s folder on the CD. When you run it, the Statistics application creates a data set of random values and then calls the ArrayStats() function to calculate the

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com

176 Chapter 4 WRITING AND USING PROCEDURES

data set’s basic statistics. The results are returned in an array, and the main program displays them in Label controls, as shown in Figure 4.3. Every time the Calculate Statistics button is clicked, a new data set is generated and its statistics are displayed.

Figure 4.3

The Statistics project calculates the basic statistics of a data set and returns them in an array.

Let’s start with the ArrayStats() function’s code, which is shown in Listing 4.2.

Listing 4.2: The ArrayStats() Function

Function ArrayStats(ByVal DataArray() As Double) As Double()

Dim Result(3) As Double

Dim Sum, SumSquares, DataMin, DataMax As Double

Dim DCount, i As Integer

Sum = 0

SumSquares = 0

DCount = 0

DataMin = System.Double.MaxValue

DataMax = System.Double.MinValue

For i = 0 To DataArray.GetUpperBound(0)

Sum = Sum + DataArray(i)

SumSquares = SumSquares + DataArray(i) ^ 2

If DataArray(i) > DataMax Then DataMax = DataArray(i)

If DataArray(i) < DataMin Then DataMin = DataArray(i)

DCount = DCount + 1

Next

Dim Avg, StdDev As Double

Avg = Sum / DCount

StdDev = Math.Sqrt(SumSquares / DCount - Avg ^ 2)

Result(0) = Avg

Result(1) = StdDev

Result(2) = DataMin

Result(3) = DataMax

ArrayStats = Result

End Function

Copyright ©2002 SYBEX, Inc., Alameda, CA

www.sybex.com