Wednesday, January 4, 2012

Procedures

As in previous versions, Visual Basic .NET supports Sub and Function procedures, which can be Private, Public, or Friend.
A procedure’s definition can include ByVal and ByRef parameters, Optional parameters, and ParamArray arguments. However, there are a few important differences that you must take into account when porting a legacy application to avoid subtle bugs and when building an application from scratch to avoid unnecessary performance hits.
A major syntax change under Visual Basic .NET is that the list of arguments being passed to a procedure must be enclosed in brackets, whether you’re calling a Sub or a Function procedure:
CODES:
Note that this rule applies to procedure calls but not to language keywords, such as Throw or AddHandler. Conveniently, the Visual Studio .NET editor puts a pair of parentheses around the argument list if you forget to add them yourself. If Option Strict is on, you can’t rely on implicit narrowing conversions when you’re passing arguments to a procedure. For example, when passing a Double variable to a Single argument you
must make the conversion explicit by using the CSng function:
CODES:
ByVal and ByRef Arguments
By default, Visual Basic .NET passes arguments using ByVal, not ByRef, as was the case with previous language versions up to and including Visual Basic 6. If you’re manually porting a legacy application, you must add the ByRef keyword for all those arguments that don’t have the explicit ByVal keyword. For example, the following Visual Basic procedure
CODES:
must be translated as follows. (Note the change in data type as well.)
CODES:
The ByVal keyword is optional, but it’s a good practice to specify it, especially if the code will be used by developers who are unfamiliar with new Visual Basic .NET conventions. Even better, while porting code from an older language version, you should reconsider whether the variable should be actually passed by reference. (Visual Basic 6 developers often mindlessly omit the ByVal keyword but don’t really mean to pass all the arguments by reference.)

In most cases, an argument that should be passed by value can also be passed by reference without causing any apparent problems. The opposite isn’t true, of course; you immediately see when you’re mistakenly passing by value an argument that should be passed by reference when the caller receives an unmodified value. However, when you use an implicit ByRef where ByVal should be explicitly used in Visual Basic 6,
you’re creating a potential source for subtle bugs and also preventing the compiler from doing the best job optimizing the resulting code. If you then migrate the code in Visual Basic .NET—for example, by importing the project in Visual Studio .NET—the inefficiency and the possibility of introducing bugs persists.

Passing Arrays
In a difference from previous Visual Basic versions, you can use the ByVal keyword for array parameters as well. However, Visual Basic .NET array variables are reference types—in other words, they point to the actual memory area in the managed heap where array items are stored. So you’re passing a 4-byte pointer whether you’re passing the array by value or by reference. In all cases, all changes to array elements inside the called procedure are reflected in the original array:
CODES:
Passing an array using ByRef or ByVal makes a difference if you use a ReDim statement inside the called procedure. In this case, the original array is affected if you pass it to a ByRef argument, but it isn’t modified if you pass it to a ByVal argument. To show how this works, let’s build a procedure that takes two array arguments with different passing mechanisms:
CODES:
Array parameters must specify the rank of the incoming array. For example, the following procedure takes a two-dimensional Long array and a three-dimensional String array:
CODES:
Optional Arguments
You can define optional arguments by using the Optional keywords, as you did with Visual Basic 6 procedures. However, you must always provide an explicit default value for each optional argument, even though the default value is 0, an empty string, or Nothing:
CODES:
The IsMissing function isn’t supported under Visual Basic .NET for this simple reason: the IsMissing function returns True only when a Variant argument has been omitted, but the Variant type isn’t supported under the current version of Visual Basic. Instead, you provide a special default value for an argument and test it inside the procedure if you want to determine whether it was omitted:
CODES:
You can use − 1 as a special value if the argument shouldn’t take negative values; or you can use the largest negative or positive number for that numeric type, which correspond to the MinValue and MaxValue properties that all numeric classes expose:
CODES:
If the optional argument is a Single or a Double, you can also use the special NaN (Nota-Number) value for its default:
CODES:
The NaN value is assigned to a floating-point number when you perform operations that don’t return a real number, as when you pass a negative argument to the Log or Sqrt function. So there’s a (very small) chance that you could mistakenly pass it to a procedure, as in the following code:
CODES:

ParamArray Arguments
You can create procedures that take any number of optional arguments by using the ParamArray keyword. In a welcome improvement on previous language versions, you can define arrays of arguments of any type. (Visual Basic 6 and previous versions support only Variant arrays for ParamArray arguments.)
CODES:

In three other differences from previous language versions, notice first that ParamArray arguments are always passed by value so that any change inside the procedure itself doesn’t affect the caller. Second, you can never omit a parameter to a procedure that expects a ParamArray:
CODES:
Interestingly, the ParamArray parameter is an array in all aspects, and you can apply to it all the methods defined for arrays in the .NET Framework. Consider the following function, which returns the minimum value among all the arguments passed to it: ‘ Note: this routine raises an error if no argument is passed to it.
CODES:
The .NET Framework offers a Sort method that can sort an array of any type, so you can rewrite the MinValue function in a more concise (though not necessarily faster) way:
CODES:
Returning a Value
Functions can return a value by assigning it to the function’s name (as you do in Visual Basic 6) or by using the new Return statement:
CODES:
The Return statement is especially handy when a function has multiple exit points because it saves your having to write an explicit Exit Function statement. A Return keyword without arguments exits a Sub procedure immediately, even though it doesn’t offer any advantage over the Exit Sub keyword. You can also use the Return keyword to return arrays, as you can see in this code:
CODES:
A reason to prefer the new Return statement to the old syntax is that you can then change the function name without also having to modify all the occurrences of the function name inside the procedure. It’s a little detail that can save you some time during the refining phase. 

As with previous versions, Visual Basic .NET allows you to use a function’s name as a local variable inside the procedure, as I’ve done in the first version of the MinValue function in previous section. In many cases, this tactic spares you the trouble of using a local variable declaration and allows you to write more concise code. Microsoft documentation states that the Return statement can improve performance because the local variable named after the function can prevent the Just-in-Time (JIT) compiler from optimizing your code. I have never observed a substantial difference in performance between the two approaches, at least in small routines used for my benchmarks, but this is yet another reason for using the new Return statement when possible.