Export (0) Print
Expand All

New Features in Visual Basic.NET: Variables, Types, Arrays, and Properties
Ted Pattison
I
n the February 2001 issue of MSDN® Magazine, I wrote an article discussing the new Microsoft .NET Framework and the role that Visual Basic.NET plays in building software for this platform. In that article I discussed the motivations that led Microsoft to reinvent their development platform and the underlying reasons why the Visual Basic® language required such a drastic overhaul. This month I'd like to build on that article, so I'll assume you've read it (see "Visual Basic.NET: New Programming Model and Language Enhancements Boost Development Power," or that you already have a high-level understanding of the .NET platform.
      While my February article was more of an overview of the common language runtime (CLR), this month I am going to concentrate on specific programming features that are new to Visual Basic.NET. Once you've learned how to use all of these new features, I'm sure you'll agree that Visual Basic.NET is a much better tool than its predecessors. However, I couldn't possibly cover all the powerful new features in a single column. So, this month's column will be the first installment in a series that will run over the next few months.
      If you're one of the many programmers who spent the better part of the last decade using Visual Basic, Visual Basic.NET will seem somewhat familiar, but at the same time foreign. While you can draw on many of your existing skills and intuitions, there are plenty of others that you'll have to let go.
      Many companies have a considerable investment in software written in Visual Basic 6.0. These companies face important decisions about what to do with existing projects. As I go through the various new features of Visual Basic.NET, keep an eye on the issues that affect porting code to Visual Basic.NET. As you'll see, there are many issues to consider. While the language has become far more consistent, powerful, and elegant, you'll find that transforming a well-written Visual Basic 6.0 project into a well-written Visual Basic.NET project requires significant effort and expertise. Hopefully, you'll have the good fortune to start a Visual Basic.NET project from scratch in the not-too-distant future.

New Conveniences

      I'd like to start by introducing the new syntax for declaring and initializing a variable with a single line of code. While many new programmers will probably take this syntax for granted, those of you who are accustomed to using two lines to declare and initialize your variables since the good old days of Visual Basic 1.0 will be very thankful.
      Here are three examples that take advantage of the Visual Basic.NET syntax.
Dim x As Integer = 10
Dim obj1 As Class1 = New Class1
Dim obj2 As New Class2
Note that, unlike the first two lines, the last line in this example is legal syntax in Visual Basic 6.0 as well as Visual Basic.NET. However, it's important to note that the As New syntax is treated differently in Visual Basic.NET. With Visual Basic 6.0, many experienced developers have frowned upon the As New syntax because it results in lazy initialization, which can cause slower performance and can also make it more difficult to debug an application.
      The good news is that the As New syntax doesn't result in lazy initialization in Visual Basic.NET and, therefore, does not present the same problems. When you look at the previous example, you should see that the third line, which uses the As New syntax, is effectively the same as the line before it. When you use the As New syntax in this fashion, the object gets created and initialized and then it is assigned to your variable before the next line begins to execute.
      The convenient new initialization syntax can also be used for fields inside the definition of a class or a structure. As you can probably guess, the syntax looks like this:
Class Class1
  Private Field1 As Integer = 10
  Public Field2 As Class1 = New Class1
End Class
      Also note that when you declare several variables on the same line, they always have the same type. Look at this example:
Dim x, y, z As Integer
All three variables are typed to integers. You no longer have to worry about the first two variables being accidentally typed to variants. In fact, you never have to worry about anything being typed to a variant because that type is not part of the CLR programming model. The universal type is now System.Object.
      One of my favorite additions to the syntax of Visual Basic.NET is the ability to pass a return value from a function back to its caller using the Return statement. Take a look at the following function:
Function MyFunction() As String
  Return "This is my return value"
End Function
      As is the case with other languages such as C, the Return statement halts method execution and returns control back to the caller. I find Return statements much more convenient than what you were forced to use in earlier versions of Visual Basic. As you'll remember, in earlier versions a return value is passed back to the caller by assigning it to the function name inside the body of the function. With a Return statement in Visual Basic.NET, you change the name of a function or copy and paste code from one method to another without the need to search through the function body and change each occurrence of the function's name.

Removing Inconsistencies

      As I covered in my February article, Visual Basic.NET neither requires nor allows you to use the Set keyword when assigning an object to a reference variable. Here is one area in which the compilers for Visual Basic 6.0 and Visual Basic.NET are in total disagreement. Examine the following code:
Dim obj1, obj2, obj3, obj4 As Class1
obj1 = New Class1
obj2 = obj1
Set obj3 = New Class1
Set obj4 = obj3
The second and third lines represent valid code in Visual Basic.NET, yet they result in runtime errors when compiled in Visual Basic 6.0. The fourth and fifth lines represent code that's valid in Visual Basic 6.0, yet they won't even compile in Visual Basic.NET. As you can imagine, the Set statement requires some attention when migrating from Visual Basic 6.0 to Visual Basic.NET.
      After writing code using Visual Basic.NET for over six months now, I still find myself adding Set statements where they don't belong. Old habits die hard. There have also been times where my professional endeavors have forced me to write code using Visual Basic.NET and Visual Basic 6.0 in the course of the same day. It's really hard to jump back and forth.
      Another syntactic change that raises migration issues is the use of parenthesis in parameter passing. Over the years, many of you have become accustomed to the rules and idiosyncrasies of calling a function versus calling a subprocedure. The Visual Basic.NET team decided it was time for a change that promoted consistency.
      The rules of parameter passing in Visual Basic.NET are very simple. Whenever you call a function or a subprocedure in which the caller is going to pass one or more parameters, the parameters must be enclosed in parenthesis. When calling a function or subprocedure that takes no parameters, the use of parenthesis is optional. You have to agree that these rules are much easier to learn and far more straightforward than those in earlier versions of Visual Basic.
      It is also important to note that the default convention for parameter passing has changed. For example, how is a parameter passed to the caller when a method is defined in this manner?
Sub Method1(Param1 As Integer)
  ' implementation
End Sub
In all previous versions of Visual Basic, Param1 is passed by reference. That means that the implementation of Method1 could make a change to this integer parameter that would be seen by the caller. In Visual Basic.NET, the default parameter passing convention has been changed to ByVal. That means that a copy of Param1 is passed to the implementation of Method1 and changes are never visible to the caller. This change in the default calling convention can obviously break the semantics of some code written in Visual Basic 6.0 when it's ported over to Visual Basic.NET.
      Note that the parameter being passed in the previous example is a value type. Primitives types such as Byte, Integer, Double, and Boolean are all examples of value types. User-defined enumerations and structures are also examples of value types. Value type instances are always copied when they are passed as ByVal.
      Things work much differently when you're passing parameters based on class types. Parameters based on classes are reference types as opposed to value types. They are used to pass object references. For example, if a caller passes an object reference to a method using a ByVal parameter, the method implementation can change the state of the object. This change in state would be visible to the caller. As a result, it's very important to distinguish between values types and reference types when you're designing parameters that must either be declared as ByVal or ByRef.
      While the use of optional parameters in methods is still allowed, it is an area that requires close attention. Unlike in Visual Basic 6.0, the use of an optional parameter now requires a default value. Along the same lines, Visual Basic.NET does not support the IsMissing function. This means you cannot differentiate between a call that omits the parameter and a call that passes the default value for the parameter.
      If you're considering designing methods with optional parameters, you should evaluate the use of method overloading as a suitable alternative. Once you've learned the benefits and syntax of method overloading, you might decide to discontinue designing methods with optional parameters.

Higher Levels of Type Safety

      Most developers who are serious about Visual Basic advocate including an Option Explicit statement at the top of every Visual Basic-based source file. The use of Option Explicit has always separated real developers from casual users. Unlike previous versions, Visual Basic.NET enables Option Explicit by default.
      Option Strict is another compile-time feature that is enabled by default in Visual Basic.NET. The Option Strict feature disallows implicit narrowing conversions. A narrowing conversion is a conversion that could result in the loss of data or precision. Look at the following example:
Dim x As Double 
x = 20.1
Dim y As Integer
' implicit narrowing conversion
y = x  ' doesn't compile under Option Strict
This code compiles in Visual Basic 6.0. However, it doesn't compile in Visual Basic.NET when Option Strict is enabled. Option Strict enforces a policy under which programmers must make their intentions about the type clear. An explicit cast allows a programmer to tell the compiler, "I know there's a narrowing conversion, but I don't expect it to be a problem." Here's how to rewrite the previous code so that it compiles:
Dim x As Double 
x = 20.1
Dim y As Integer
' explicit narrowing conversion
y = CInt(x) ' compiles under Option Strict
      Another big change to the language is that If statements are now short-circuited. This represents a valuable optimization, but once again it's important to understand how it could break Visual Basic 6.0-based code when ported to Visual Basic.NET. Look at the following code:
If Function1() And Function2() Then
  ' statement block
End If
If Function1 evaluates to False, is it really necessary to execute and evaluate Function2 to determine whether to execute the statement block inside the If Then construct? The answer is obviously no. However, earlier versions of Visual Basic did not perform short-circuiting and, consequently, Function2 would be executed in all situations. Visual Basic.NET conducts short-circuiting that results in Function2 not being executed if Function1 returns false. This is yet another behavioral difference that could prevent Visual Basic 6.0-based code from running correctly when compiled under Visual Basic.NET.
      At this time I'd like to point out another improvement to type safety within the language. You should note that the previous code example will not compile if either Function1 or Function2 has a return type that is anything other than Boolean. With the new higher levels of type safety, you can no longer run conditional tests on integer values. For example, the following code, commonly used in Visual Basic 6.0, will not compile in Visual Basic.NET.
Dim x As Integer
' set x to 0 for false or anything else for true
If x Then
  ' execute statement block
End If
The new levels of type safety enforced by Option Strict require you to work exclusively in terms of Boolean values when you use constructs such as If statements and Do loops.
      When it comes to using logical comparison operators such as Not, And, Or, and Xor, you are also restricted to using Boolean values as both input and output. This is different from previous versions of Visual Basic where these operators could be used for either logical comparisons or bitwise comparisons. Here's another example of code that compiles in Visual Basic 6.0 but results in a compile-time conversion error in Visual Basic.NET.
Dim x As Integer
Dim y As Integer
Dim z As Integer
x = 3
Y = 6
z = x And y ' doesn't compile
      Visual Basic.NET provides four new bitwise comparison operators: BitNot, BitAnd, BitOr, and BitXor. These operators allow you to perform bit-twiddling operations in a type-safe manner. For example, you would use one of these operators to OR the bits in two integer values together or to apply a bitmask. Here's an example of using BitOr and BitAnd:
Dim x As Integer = 3
Dim y As Integer = 6
Dim z As Integer
' OR bits together
z = x BitOr y   ' z now equals 7
' AND to find intersection of bits
z = x BitAnd y  ' z now equal 2

Programming Arrays

      There has been a fairly dramatic change to the fundamental syntax for declaring and using arrays. First of all, arrays are always zero-based. You can no longer create an array with a lower bound of 1. As a consequence, the Option Base statement has been removed from the language.
      Furthermore, when you declare an array you must initialize it with its number of elements instead of its upper bound. Here's a simple example:
' declare an array with 3 elements from 0 to 2
Dim array1(3) As Integer 
array1(0) = 2
array1(1) = 4
array1(2) = 8
      If the array in the previous example were declared in Visual Basic 6.0, it would have four elements with positions ranging from 0 to 3. In Visual Basic.NET, this array has three elements with positions ranging from 0 to 2. Under Visual Basic.NET your code will experience a runtime exception if it attempts to access an element at position 3 in the previously named array, like so:
' index out of range exception
array1(3) = 16
      Visual Basic.NET provides a new syntax for initializing arrays. You can declare and initialize an array in just a single line of code, like this:
' new array initialization syntax
Dim array1 As Integer() = {2, 4, 8}
As in earlier versions of Visual Basic, you can enumerate through an array using a For Each loop.
Dim x As Integer
For Each x In array1
  Console.WriteLine(x)
Next
Alternatively, you can loop through an array using a For loop and the array's Length property, like this:
Dim i As Integer
For i = 0 To (array1.Length - 1)
  Console.WriteLine(array1(i))
Next i
      The CLR and Visual Basic.NET support multidimensional arrays in addition to single-dimensional arrays. For example, if you want a two-dimensional array, you could define it using one of these three techniques:
Dim array1(2, 2) As Integer

Dim array2 As Integer(,)
Redim array2(2, 2)

Dim array3 As Integer(,) = { {12, 24}, {10, 20} }
      When it comes to programming with arrays, you should understand that far more than just the syntax has changed. The way arrays are handled by the underlying runtime is totally different. In the CLR, arrays are always allocated as heap-based objects. When you add a parameter to a method based on an array type, it means you're passing an object reference as opposed to passing a copy of the array.
      Here are three methods that pass array references back and forth:
Sub Method1(ByRef array1 As Integer())
  ' implementation
End Sub

Sub Method2(ByVal array1 As Integer())
  ' implementation
End Sub

Sub Method3(ByVal array1 As Integer(,))
  ' implementation
End Sub
Method1 passes an array reference in both directions and would typically be used to pass an array reference back to the caller. Method2 and Method3 pass array references from the caller to the method implementation. Note that the parameter in Method2 is typed as a single-dimensional array, while the parameter in Method3 is typed as a two-dimensional array.
      Every array object inherits its core implementation details and a set of public members from the system-supplied class, System.Array. You should check out the documentation on System.Array in the .NET platform SDK. This class provides built-in functionality for performing common array-oriented tasks such as clearing, copying, cloning, searching, and sorting. Here's an example of using some of the methods exposed by the System.Array class:
Dim array1 As Integer() = {4, 2, 8, 1}
Dim array2 As Integer()
' cloning an array
array2 = CType(array1.Clone(), Integer())
' clearing an array
System.Array.Clear(array1, 0, array1.Length)
' sorting an array
System.Array.Sort(array2)
' sorting an array in reverse order
System.Array.Reverse(array2)
      Note that you can perform a sort operation on any array as long as the type the array is based on implements the IComparable interface. Core CLR types such as Integer, Double, String, and Date have been designed to implement IComparable. If you want to sort arrays based on your own custom types, you simply need to implement the IComparable interface in your classes and structures.

A New Syntax for Properties

      I'm sure you're familiar with defining and using properties in earlier versions of Visual Basic. While the motivation for using properties remains exactly the same, the syntax for defining them has changed. Properties are now defined in terms of a property construct that has a Set block and/or a Get block.
      The following is an example of a class with a private field and a public property:
Class Class1
  ' private field
  Private m_Name As String
  ' public property
  Public Property Name As String
    Get
      Return m_Name
    End Get
    Set
      If Value <> "" Then ' Value is intrinsic variable
        m_Name = Value
      End If
    End Set
  End Property
End Class
Note that the Set block contains the intrinsic variable named Value. Visual Basic.NET uses this intrinsic variable to pass the property value assigned by the client to your property implementation inside the Set block. Also note that with Visual Basic.NET, there is no longer the confusion surrounding when to use a Property Set versus a Property Let.
      A property must be ReadWrite, ReadOnly, or WriteOnly. The example shown previously demonstrates a ReadWrite property. The following examples show how to create a ReadOnly and WriteOnly property, respectively.
' ReadOnly property has Get but no Set
Public ReadOnly Property FullName as String
  Get
      Return m_FirstName & " " & m_LastName
  End Get
End Property
' WriteOnly property has Set but no Get
Public WriteOnly Property Password as String
  Set
    m_Password = Value
  End Set
End Property
Note that you must use the keywords ReadOnly or WriteOnly when you elect to omit either the Get block or the Set block.

Indexed Properties and Default Properties

      A property can be assigned one or more indexes. This allows a property to exhibit array-like qualities. For example, look at the following class:
Class Class1
  Private m_Names As String() = {"Ted", "Fred", "Jed"}
  ' an indexed property
  Readonly Property Item(Index As Integer) As String
    Get
      Return m_Names(Index)
    End Get
  End Property
End Class
From the client side, you can access the Item property with the following code:
Dim obj As New Class1
Dim s1 String
s1 = obj.Item(0)
      Taking this example one step further, you can mark an indexed property as a default property for a class. To do this, you simply add the Default keyword to the last example, like this:
Default Readonly Property Item(Index As Integer)…
Once you've marked an indexed property with the Default keyword, client-side code can omit the property name and use an object reference as if it were an array:
Dim obj As New Class1
Dim s1 String
s1 = obj(0)
      You should be aware of one important restriction with regard to default properties. A property that has no index cannot be marked as a default property. This restriction arises in Visual Basic.NET because you can't use the Set statement to assign object references. Since the Set statement has been removed from the language, nonindexed default properties would create an ambiguity that could not be resolved.

Conclusion

      As you have seen, Visual Basic.NET is in many ways a very different language from its predecessors. It is far more consistent and type-safe. In addition, Visual Basic.NET makes it easier to write code that is both manageable and readable. And even though it might take you some time to get accustomed to all those extra compile-time checks, it will save you valuable time chasing down bugs during your testing and debugging cycles.
      The good news is that Visual Basic has been transformed into a better, more powerful language. The bad news is that a nontrivial project written in Visual Basic 6.0 will require quite a bit of work if you plan to migrate it to Visual Basic.NET.
      While this month's column addressed many of the core changes to the fundamental syntax of Visual Basic, there is so much more to cover. I still haven't discussed any of the sexy, new object-oriented features such as shared methods, method overloading, parameterized constructors, and inheritance. So stay tuned for the next installment of Basic Instincts.

Send questions and comments to Ted at instinct@microsoft.com.
Ted Pattison is an instructor and researcher at DevelopMentor (http://www.develop.com), where he comanages the Visual Basic curriculum. The second edition of Ted's book, Programming Distributed Applications with COM and Microsoft Visual Basic 6.0, was published by Microsoft Press in June 2000.

From the May 2001 issue of MSDN Magazine

Show:
© 2014 Microsoft