RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Fall In Love with Visual Basic All Over Again in Visual Studio 2008 : Page 3

Discover anew why Visual Basic is one of the most successful programming languages in history by exploring some of the new features Microsoft added to Visual Studio and Visual Basic.

Updated Ternary Operator
Dictionary.com defines ternary as "consisting or involving three; threefold; triple." For VB developers, the ternary IIf operator has provided a concise method for making conditional assignments. The IIf operator accepts three parameters. The first parameter is a Boolean expression. When the Boolean expression evaluates to True, the operator returns the value of the second parameter, and when it evaluates to False the operator returns the value of the third parameter.

For example, the following IIf operator code alternates the color string assigned to a variable. If the initial value of strColor is "White," the operator returns the second parameter value, "Gray," Conversely, an initial value of "Gray" results in the return of the third parameter, "White."

   strColor = IIf(strColor.Equals("White"), "Gray", "White").ToString()
Watch out, though; there is a potential side effect to using the IIf operator. The standard IIf operator always evaluates both the possible return values. Therefore, if either the second or third parameters are function calls, they will be called and evaluated.

Take a moment to review the ComputeOvertimePay and ComputeRegularPay functions shown below. Both work similarly, computing regular or overtime pay based on a parameter containing the number of hours worked. If you call ComputeOvertimePay with intHours <= 40, it throws an exception. The ComputeRegularPay function works similarly.

   Private Function ComputeOvertimePay(ByVal intHours As Integer) As Decimal
       If intHours > 40 Then
           Return CDec(intHours * 1.5 * c_PayPerHour)
           Throw New ApplicationException("Hours too low.")
       End If
   End Function
   Private Function ComputeRegularPay(ByVal intHours As Integer) As Decimal
       If intHours <= 40 Then
           Return intHours * c_PayPerHour
           Throw New ApplicationException("Hours too high.")
       End If
   End Function
Now consider the code below that uses the ternary operator to compute an employee's wages. Passing a value of 32 to both functions causes the ComputeOvertimePay function to throw an exception.

   pay = CDec(IIf( intHours > 40, _
      ComputeOvertimePay(32), ComputeRegularPay(32)))
In short, the IIf ternary operator functions similar to the more verbose code shown below. In other words, the single line of code above is the same as writing:

   intHours1 = ComputeOverTimePay(32)
   intHours2 = ComputeRegularPay(32) 
   If intHours > 40 Then
       Return intHours1
       Return intHours2
   End If
VB9 introduces a new and syntactically simplified form of the ternary operator called If (note the loss of the extra "I") that prevents the unwanted behavior by evaluating only the appropriate expression—the other expression is never evaluated. In the code sample below, because the first expression (intHours > 40) evaluates to False, the new If operator calls only the ComputeRegularPay function.

   inHours = 32
   pay = CDec(If( intHours > 40, _
In other words, it functions identically to:

   If intHours <= 40 Then
       Return ComputeOverTimePay(32)
       Return ComputeRegularPay(32)
   End If
Another use of the ternary If is to replace NULL values. In the code sample below, if the initial value of str is Nothing, then the If operator returns an empty string.

   str = If(str, String.Empty)
The new ternary operator (If version) is available even if you target the 2.0 framework.

Better Nullable Types
Prior to VS2005, there was no elegant way to make a value type nullable—you needed to maintain additional state variables to keep track of whether a value type was set. The 2.0 version of the framework added the generic Nullable type, which wasn't elegant but provided a workable solution. The Nullable generic type functions as demonstrated in the code sample below.

   Dim int As Integer
   Dim intNullable As Nullable(Of Integer)
   intNullable = Nothing
   intNullable = 65
   int = intNullable.Value
VB9 adds Nullable types. In VB9, to mark a type as Nullable, add a question mark after the type name in the variable declaration. As shown in the code sample below, it is used more similarly to a standard type. Nullable types are different from normal value types because they can be set to Nothing and they expose an additional function named HasValue that you can use to query for the Nothing-ness of the type. Note that you must perform a typecast to assign the value to a standard (non-nullable) variable.

   ' intNullable is a NULLable type
   Dim intNullable As Integer?
   Dim int As Integer
   intNullable = 65
   ' a type cast is necessary because the types are different
   int = CInt(intNullable)
   intNullable = Nothing
This is a great enhancement for anyone who uses types that can be set to NULL. It prevents the overhead and error-prone nature of using state variables to keep track of whether a value has been set, and it supplants the use of the Nullable generic type, which is not as elegant a solution as the new Nullable types.

Partial Methods
Imagine you have just created a code generation template to standardize the way you access table data. Your tool produces a set of class files that can be compiled into a data access assembly. You obviously don't want developers to recode their extensions to the generated classes each time the code is generated, so the class files produced by the tool are declared as partial classes—the class's functionality is extended by modifying another file containing the other portion of the partial class.

A further requirement of the generated classes is that an event will be fired when a field value changes. Typically, you'd handle this in older versions of VB by defining delegates to be invoked when field values change. This approach requires two things. First, the calling code, at runtime, must register methods to be called when the field value is changed. Second, the generated code needs to ensure that the calling code has in fact registered a valid method before invoking the delegate. This proves somewhat cumbersome for both the author of the generated code and the author of the calling code.

Partial methods solve these problems. Using partial methods, you would declare a stub (no code) for the partial method in the generated class as part of the code generation process. This partial method can be called from code in the generated class. Then, in the non-generated portion of the partial class, you implement only those partial methods you are interested in handling. At compile time, only calls to partial methods with an implementation get compiled into the final assembly. The compiler removes method calls made to unimplemented partial methods, streamlining the resulting code. This is similar to the delegate solution, except that the cost of detecting an available method to call is paid at compile time instead of at runtime. In addition, the code is more readable because the partial methods solution doesn't require the various constructs to manage the delegates.

The two code samples below demonstrate the use of partial methods in a code generation environment. The first sample (from the DataLayer_auto.vb file in the downloadable sample code) contains a partial class named "DataLayer" created by a code generator. The Set portion of the SSN property declaration calls the SSNChanged partial method.

   ' DataLayer_auto.vb file
   Partial Public Class DataLayer
       . . .
       Private m_strSSN As String
       Public Property SSN() As String
               Return m_strSSN
           End Get
           Set(ByVal value As String)
               m_strSSN = value
           End Set
       End Property
       . . .
       Partial Private Sub SSNChanged(ByVal newValue As String)
       End Sub
   End Class
The second sample (from the DataLayer.vb file in the downloadable code) also contains an implementation of the SSNChanged method. It's implemented here so that it will not be overwritten when the code generation tools generates a new version of the DataLayer_auto.vb file.

   ' DataLayer.vb file
   Public Class DataLayer
       Private Sub SSNChanged( _
          ByVal newValue As String)
           If newValue = "-1" Then
Figure 7. Implemented Partial Method: At this breakpoint at runtime, the IDE highlights the implemented SSNChanged method call as an executable line of code.
m_strSSN = "000000000" End If End Sub End Class
To see the SSNChanged partial method in action, set a breakpoint on the m_strSSN = value line in the Set portion of the SSN property in the generated DataLayer_auto.vb file. Run the project and click the "Partial Methods" button. When execution reaches the breakpoint, you can step into the SSNChanged method implementation (see Figure 7).

Figure 8. Unimplemented Partial Method: When the partial method SSNChanged is unimplemented, the IDE simply skips over the SSNChanged method call when stepping through the code.
Now, comment out the SSNChanged method implementation in the DataLayer.vb file and run the project again. This time, when the IDE reaches the breakpoint, it just steps over the line that calls the SSNChanged method, because it doesn't exist in the compiled version. You can see this contrasting behavior in Figures 5 (SSNChanged implemented) and Figure 8 (SSNChanged is not implemented).

This method of extending auto-generated code turns out to be both slightly faster—and more importantly—easier to maintain than the delegate method. However, there are some restrictions to using partial methods that will limit your ability to make heavy use of them. First, they are usable only inside partial classes. Second, partial methods are required to be declared as Private because the external interface of a class would need to change based on whether the partial methods were implemented. Because of these limitations, I cannot think of a good place to use partial methods outside of extending auto-generated code.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date