- Chapter 3: VB .NET Language Features
- Functional Changes
- Upgrading from VB 6.0
- Summary
Functional Changes
In addition to the syntactical changes, VB .NET behaves differently from previous versions in several respects. This section details the most significant of those changes.
Declarations
Several of the most obvious and productivity-enhancing changes in the language are related to dimensioning and instantiating variables.
As you might be aware through painful experience, in prior versions, a declaration like so
Dim x,y,z As Integer
did not result in three Integer variables. In fact, the first two variables were declared as Variant, which wastes memory and has the potential of causing type conversion problems down the road. In VB .NET, multiple declarations work as expected, and so the same declaration will result in three Integer variables. In addition, VB .NET supports the capability to use the declaration
Dim z
when the Option Strict statement is set to Off. In this case, rather than result in a Variant, the variable z is of type System.Object. Also note that unlike in previous versions, Option Explicit is defaulted to On so that variables cannot be used without first declaring them.
VB .NET also supports parameterized constructors so that objects can be initialized during the declaration. This, coupled with support for initialization during declaration, allows VB .NET to support the following types of syntax:
Dim dtAaron As New DateTime(1974, 4, 8) Dim strName As String = "Hank Aaron" Dim arTheropods() As String = {"Tyrannosaur", "Allosaurus", "Deinonychus"}
In this example, the dtAaron variable is declared as a DateTime structure and instantiated with arguments passed to the constructor specifying the day Hank Aaron hit his 715th homerun. This is shorthand for the statement:
Dim dtAaron As DateTime = New DateTime(1974, 4, 8)
In the second example, VB .NET takes care of calling the constructor for the String variable to allow developers to work with strings in a familiar fashion. The third example shows how to initialize an array of strings during declaration.
TIP
Although the constants you would expect (those prefixed with "vb") exist within the Microsoft.VisualBasic namespace, they also are exposed through enumerated types. Because the methods of the namespace expect the enumerated types, it is recommended that you use them rather than the constants.
Object Creation
Two other changes from the behavior of previous versions of VB are related to the creation of objects using the New operator. In previous versions the statement
Dim dino As New Dinosaur
would result in the compiler actually wrapping each line of code that referenced dino with a check to see whether the variable was Nothing. If so, the object was instantiated. In VB .NET, implicit object creation is not supported, and so the object is instantiated when the Dim statement is encountered. As a result, if you're going to use the New operator in a declaration, you'll want to place the declaration within the procedure in a place you know the variable will be used. In other words, it is more efficient if you defer dimensioning variables that might end up not being used, as long as possible.
In addition, in VB 6.0, the New operator actually used an internal algorithm that was more efficient to instantiate classes within the same project. In VB .NET, the CLR creates and manages all object instances, and so there is no difference when New is used with classes in the local assembly versus a remote assembly.
Finally, the CreateObject function, although still supported, will only be used to create a COM object. All managed classes are instantiated with the New keyword.
Operators
As shown in Table 3.2, VB .NET adds support for new operators. Perhaps the most significant from a keystroke-saving perspective are the operators that perform their operation and assign the result to the variable. For example, in VB 6.0, to concatenate a string on two lines, you would do the following:
Dim strMessage As String strMessage = "This is a message that will be pretty long," strMessage = strMessage & " so I'll break it up a little."
In VB .NET, this can be compacted to the following:
strMessage = "This is a message that will be pretty long," strMessage &= " so I'll break it up a little."
In addition to concatenation, addition, subtraction, multiplication, division, and exponentiation are supported using this compacted syntax.
VB .NET also does not support default properties, and so the statement
Text1 = strName
does not assign the string variable strName to the Text property of the Text1 object. As a result, you must explicitly use the property. The good news is that this means the Set and Let statements, which caused a good deal of confusion, are no longer required or supported. The one exception to default properties is in the case where the property accepts a parameter. In this instance, you can mark the property as the default so that working with objects that support collections through properties such as Item is simplified.
Operators evaluation in expressions also can be short-circuited in VB .NET so that a procedure like the one that follows can be more efficient:
Sub Insert(ByVal value As Integer, ByVal sortedArr() As Integer) Dim intIndex, intIndxSrch As Integer Dim intArrCount As Integer = sortedArr.Length `Scan for entry less than one to be inserted While intIndxSrch < intArrCount AndAlso value > sortedArr(intIndxSrch) intIndxSrch += 1 End While `Make room for new value --- move remaining items up End Sub
In this example, the Insert procedure inserts a new value in a sorted array. Note that the While loop contains the new AndAlso operator with two expressions. Using short-circuit evaluation, the second condition, which is the more expensive of the two, will not be executed if the first condition returns False. In the past, you needed to place these conditions on separate lines to gain this efficiency. Short-circuiting also is implemented with the new OrElse operator when the first expression evaluates to True.
NOTE
Although it was originally planned that the And, Or, Not, and Xor operators would only perform logical and not bitwise operations, feedback from the VB community ensured that these retain both functionalities and precedence rules as in VB 6.0.
Although not documented in VB 6.0, some developers became accustomed to using the VarPtr, VarPtrArray, VarPtrStringArray, ObjPtr, and StrPtr operators to return the memory addresses of variables of these data types. This was particularly useful when calling some Win32 API functions. These keywords are no longer supported in VB .NET.
Closely related to operators is the behavior of null values. In VB 6.0, null propagation was supported where adding or concatenating a value to Null, or a Variant that contained Null, always produced a null value. In VB .NET, Null has been replaced with Nothing, Variant with Object, and null propagation is not supported. As a result, the following behavior occurs in VB .NET when Option Strict is set to Off:
Dim x As Object Dim i As Integer i = 4 x = Nothing i = i + x ` i still equals 4
The Null keyword has been replaced with the System.DBNull class, and checking for nulls returned from databases can be done using the IsDBNull function. The equivalent syntax used in VB 6.0, where an empty string ("") concatenated to a null from a database produced an empty string, is still supported as well.
The absence of both variants and null values implies that functions that once accepted variants and possibly returned null values, such as Mid, Oct, Right, RTrim, Space, Str, Time, Trim, LTrim, UCase, and LCase, among others, now simply return their associated data types. As mentioned in Table 3.1, VB 6.0 also contained companion functions appended with $ (i.e. Trim$) that returned String values. These also have been replaced with overloaded methods that return a String.
Data Types
As discussed in Chapter 1, "The Microsoft .NET Architecture," all data types in VB .NET derive from System.Object and are therefore a part of the Common Type System (CTS) used by the CLR. VB .NET supports keywords that map to the CTS types shown in Table 1.1. This mapping is shown in Table 3.3.
Table 3.3 Data Type Mappings in VB .NET
VB .NET Data Type (with type character) |
CTS Type |
---|---|
Boolean |
System.Boolean |
Byte |
System.Byte |
Char (new) |
System.Char |
Decimal (@) (new) |
System.Decimal |
Double (#) |
System.Double |
Integer (%) |
System.Int32 |
Long (&) |
System.Int64 |
Short (new) |
System.Int16 |
Single (!) |
System.Single |
String ($) |
System.String |
As in VB 6.0, the type character can be used to automatically dimension a variable by appending it to the declaration. For example,
Dim z%
ensures that z is an Integer. However, because type characters make the code less readable, they are not recommended.
In addition, several of the data types include special characters (literal type characters) that can be used with literal values to force their representation to the appropriate type. These include C (Char), D (Decimal), R (Double), I (Integer), L (Long), S (Short), and F (Single). In other words, the statement
Dim b As Object b = "T"C
forces the variable b to the type Char.
One of the most interesting aspects of Table 3.3, though, is the representation of integers. In VB 6.0, the Integer data type was 16 bits, and so the range was from -32,768 to 32,768. However, with the inclusion of Short to fill that spot, Integer is now a 32-bit value that is equivalent to the Long data type in VB 6.0. In turn, Long is now 64 bits with a range from ±9,223,372,036,854,775,808. Although this is desired because it brings VB .NET inline with other modern programming languages and applications, such as SQL Server, keep in mind that if you're porting existing code or rewriting code from VB 6.0 you'll want to change the data types as appropriate.
The internal representation of several data types in the CLR also is different from previous versions of VB. For example, the Boolean data type in VB 6.0 equated to -1 for True and 0 for False. Now, the more standard 0 for False and 1 for True is used by the runtime. However, to maintain backwards compatibility when working in VB .NET, the older -1 and 0 will be used. Keep in mind, though, that converting a Boolean to a CTS type such as System.Int32, or passing a Boolean to another managed language, will convert the result back to a 1 for True and 0 for False. Also, as mentioned in Table 3.3, the Date data type is no longer represented as a Double; it is mapped to System.DateTime, and implicit conversion between the two is no longer supported. Explicit conversion, however, can be accomplished by using the ToDouble and FromOADate methods of the DateTime class.
Of course, as should be obvious by now, the universal data type in previous versions of VB, the Variant, has been replaced with Object. The Object keyword used in VB 6.0 to refer to a late bound object has been subsumed into this definition.
Finally, as shown in Table 3.1 the Currency data type is no longer supported in VB .NET and has been replaced with the Decimal data type.
Arrays
There are several important changes in VB .NET with regards to arrays. First, the lower bound of an array can no longer be set using the Option Base statement. As a result, the syntax
Dim s(0 to 10) As String
no longer is supported because all arrays are zero bound.
NOTE
The designers of VB .NET originally planned that the array declaration would specify the number of elements and not the upper bound. In other words, the declaration
Dim s(10) As String
would produce an array of 10 elements with indices from 0 through 9. However, this changed shortly before the beta 2 was released based on feedback from the VB community.
Also in VB .NET, by default, all arrays are variable-sized arrays, so they all can be resized using the ReDim statement. However, the ReDim statement cannot be used in place of the Dim statement to dimension an array as in VB 6.0.
In addition, as seen previously, an array can be populated in the declaration using the { } brackets like so:
Dim sosaHomers() As Short = {66, 63, 50, 64}
This syntax replaces the Array function formerly used to populate an array with arguments passed to the function. VB .NET still supports multidimensional arrays; however, the number of dimensions (referred to as the rank) must be set in the Dim statement. For example, the code
Dim arPlayers(,) As String ReDim arPlayers(1, 3) arPlayers(0, 0) = "Pitcher" arPlayers(0, 1) = "Catcher" arPlayers(0, 2) = "1B" arPlayers(0, 3) = "2B" arPlayers(1, 0) = "Roger Clemens" arPlayers(1, 1) = "Mike Piazza" arPlayers(1, 2) = "Mark McGwire" arPlayers(1, 3) = "Eric Young"
works in VB .NET because the comma in the Dim statement denotes that there are two dimensions to the array. The same declaration without the comma works in VB 6.0. As in VB 6.0, VB .NET also allows you to change the upper bound of the last dimension in the array while preserving the contents of the array, and so the statement
ReDim Preserve arPlayers(1, 4)
immediately after the preceding code example successfully adds another element to the second dimension. The Preserve keyword ensures that the data is preserved.
TIP
As in VB 6.0, if you don't know the rank the array will have at development time, then dimension the array simply As Array or As Object.
VB .NET also still supports ragged arrays or arrays of arrays, which, like multidimensional arrays, allow you to store tabular type data but allow a varying number of elements. For example, the code in Listing 3.1 shows how you would create a ragged array by dimensioning the array As Object and then placing arrays inside it.
Listing 3.1 Creating Ragged Arrays in VB .NET. To create a ragged array use the Object type.
Dim a As Object Dim b(0) As String Dim c(1) As String Dim obj As Object Dim obj1 As Object ReDim a(2) ` Fill the inner arrays c(0) = "c1" c(1) = "c2" b(0) = "b1" ` Set the elements of the outer array a(0) = b a(1) = c a(2) = "Third element" ` Traverse the contents of the array For Each obj In a If IsArray(obj) Then For Each obj1 In obj Console.WriteLine(obj1.ToString) Next Else Console.WriteLine(obj.ToString) End If Next
Note that Option Strict must be set to Off for this code to compile because late binding is occurring in the array referenced as a. Although this works in VB .NET, you might want to stick with multidimensional arrays because ragged arrays are not CLS compliant, and passing such an array to managed code written in another language might not work.
Many of these changes are made possible by the fact that all arrays in VB .NET are ultimately derived from System.Array. This class supports several shared methods that can be used to manipulate arrays as well as instance methods that can return data on a particular array. As you would expect, because System.Array is a reference type, assigning a variable to an existing array creates a reference to the array rather than a copy of it. To get a copy, you can use the Clone method of the Array class.
TIP
Shared methods can be called on a class without first instantiating an object from the class. This allows the class to contain various helper functions that can be used in a variety of situations. Conversely, instance methods are those that work on the current instance of the class.
For example, the declaration of the arPlayers array in VB .NET is actually translated to this:
Dim arPlayers As Array arPlayers = Array.CreateInstance(GetType(String), 2, 4)
Note that the sizes of the dimensions as specified in the last two arguments to the CreateInstance method are set to 2 and 4, rather than 1 and 3 as you might expect. This is the case because arrays in the CLR typically are declared using the size of a dimension rather than the upper bound.
Even if the array is created in standard VB .NET syntax, all the Array methods and properties are available, the most important of which are shown in Table 3.4.
Table 3.4 Array Class Members
Member |
Description |
---|---|
BinarySearch |
Shared method that searches a one-dimensional array for a specific element |
Clear |
Shared method that sets a range of the array to zero or Nothing |
Copy |
Shared method that copies a section of one array to another array |
CreateInstance |
Shared method that initializes a new instance of the Array class |
IndexOf |
Shared method that returns the index of the first occurrence of a given value in a one-dimensional array |
LastIndexOf |
Shared method that returns the last index of the first occurrence of a given value in a one-dimensional array |
Reverse |
Shared method that reverses the order of elements in a one-dimensional array |
Sort |
Shared method that sorts the elements of a one-dimensional array |
IsFixedSize |
Instance property that returns True if the array is fixed size |
IsReadOnly |
Instance property that returns whether the array is read-only |
IsSynchronized |
Instance property that returns whether access to the array is thread-safe |
Length |
Instance property that returns the total number of elements in all dimensions of the array |
Rank |
Instance property that returns the number of dimensions |
SyncRoot |
Instance property that returns an object that can be used to synchronize access to the array |
Clone |
Instance method that creates a copy of the array |
CopyTo |
Instance method that copies all or part of a one-dimensional array to another array |
GetLength |
Instance method that returns the number of elements from a specified dimension of the array |
GetLowerBound |
Instance method that returns the lower bound of a specified dimension of the array |
GetUpperBound |
Instance method that returns the upper bound of a specified dimension of the array |
GetValue |
Instance method that returns the specified values in the one-dimensional array |
SetValue |
Instance method that sets the specified values in the one-dimensional array |
Initialize |
Instance method that initializes every element of a value-type array if it has a constructor |
To illustrate the use of the Array class, consider the procedure in Listing 3.2 used to find a specific item in an array.
Listing 3.2 Using the Array Class. This method exercises some of the functionality of the Array class to sort and search the array.
Function FindVal(ByVal value As Object, ByRef arr As Array) As Integer Dim intIndex As Integer If Not IsArray(Arr) Then Throw New ArgumentException( _ "Argument is not an array or array has no elements") Return -1 End If Try ` Sort the array Array.Sort(arr) ` Do a search intIndex = Array.BinarySearch(arr, value) Catch e As Exception Throw New Exception( _ "There was an error working with the array: " & e.Message) Return -1 End Try ` Negative if not found If intIndex < 0 Then Return -1 Else Return intIndex End If End Function
In this example, note that the FindVal procedure accepts an argument declared As Object to search for in the array, along with the array, and returns the index at which the value was found. The new IsArray function is used to determine whether the array has been initialized. If so, the array is first sorted using the Sort method and then a binary search is performed using BinarySearch. The return value from BinarySearch will be negative if the value is not found. Note that by definition, the BinarySearch method is case sensitive. In this case, the method returns the index of the element if the value is found, and a -1 if not. Since the array is passed to the method by reference, the array will be sorted when the method returns and the index will point to the new position of the value within the sorted array.
NOTE
Many of the methods of the Array class work only with one-dimensional arrays. If you attempt to call these methods on multidimensional arrays, exceptions will be thrown. To work with, sort, and search multidimensional arrays, you'll need to employ your own techniques. Some of these can be found in Chapter 24 of my book Pure Visual Basic.
Alternatives to Arrays
Although arrays in VB .NET are easily mapped to the System.Array type, they are not always ideal for manipulating data within your application. To provide more choice and flexibility in working with collections of data, the Services Framework includes the System.Collections and System.Collections.Specialized namespaces. In fact, using these prebuilt collections can offload some of the array manipulation code you would normally write and can greatly improve performancefor example, when needing random access to an in-memory collection of data.
You can think of these namespaces as containing prebuilt data structures for handling data. For example, the System.Collections namespace contains the SortedList class that can be used to store a collection of values and associated keys. As the name implies, the data added to the collection is sorted automatically by the key, and the class contains various methods to access the data either by the key, index, or value. The following code illustrates the use of the SortedList class to store a collection of names:
Dim employees As New SortedList() Dim i As Integer employees.Add(3322, "Steve Lake") employees.Add(6743, "Jody Davis") employees.Add(1233, "George Mitterwald") employees.Add(1341, "Tim Hosey") For i = 0 To employees.Count - 1 Console.WriteLine("{0}:{1}", _ employees.GetByIndex(i).ToString, employees.GetKey(i).ToString) Next
When the For loop is executed, the names are printed in order by their key value.
NOTE
Many of the collection classes include constructors that accept the initial capacity of the collection. Although the capacity of the collection is increased automatically as required through reallocation, this can be used to increase performance when you know ahead of time how many members the collection will contain. In the ArrayList and SortedList classes, the TrimToSize method can subsequently be called to minimize a lists memory overhead.
Although explicating all the classes and their members is beyond the scope of this book, the other types of collections, their descriptions, and uses can be seen in Table 3.5.
Table 3.5 Collection Classes in the System.Collections and System.Collections.Specialized Namespaces
Class |
Description |
Use |
---|---|---|
ArrayList |
Implements an array that is dynamically sized and able to be sorted |
When you need to access the values by index |
BitArray |
Manages a compact array of bit values represented as true (1) or false (0) |
When you need to track a series of switches |
Hashtable |
Implements a collection of values and associated keys where the key is hashed |
When you need to quickly access a value by the key for large collections |
HybridDictionary |
Implements a ListDictionary when the collection is small and dynamically switches to a HashTable as the collection grows for performance reasons |
|
ListDictionary |
Implements a linked-list of key-value pairs |
When you have fewer than 10 values and need access to them by the key |
NameValueCollection |
Implements a sorted collection of String keys and values |
When you need access to the values by the hash code of the key or the index |
Queue |
Implements a first-in, first-out collection of objects |
When you need to access the values in FIFO order |
SortedList |
Implements a collection of key-value pairs sorted by the key |
When you need to access the values in order by the key |
Stack |
Implements a last-in, first-out collection objects |
When you need to access the values in LIFO order |
StringCollection |
Implements a collection of strings |
-When you need to store strings and access them by index |
StringDictionary |
Implements a hashtable where the key and value are both strings |
When you need to access a large collection of strings by key |
The only disadvantage to using most of the collection classes, except the last two shown in Table 3.5, is that they only accept keys and values of type Object. As a result, you'll need to convert the values to the appropriate type when retrieving them from the collection.
Structures
In VB 6.0, data could be grouped together in a user-defined type (UDT) using the Type statement. The UDT then could be instantiated and populated using dot notation and used as a return value from a function or as an argument to a procedure. Further, all the members of the type were public, and the type could only contain fieldsno methods, properties, or events.
In VB .NET, the UDT has been replaced with the Structure. A Structure is much more powerful than a UDT. It can contain fields, methods, properties, events, enumerations, and even implement interfaces. In fact, a Structure can be thought of as simply a lightweight class. The primary difference is that a Structure is always a value type as discussed in Chapter 1, and is therefore always copied when used in an assignment statement or passed by value to a procedure. In addition, structures do not support inheritance, and you needn't use the New operator with structures because it is called implicitly in the declaration. However, structures can support constructors, as discussed in Chapter 4. As an example, consider the structure declaration shown in Listing 3.3.
Note that this structure encapsulates information about a baseball player. It contains public fields to represent the attributes of an offensive playersuch as the number of hits (H), doubles (D), triples (T), homeruns (HR), and so onas would be supported in a VB 6.0 Type. In addition, however, it contains public properties to expose the batting average (AVG) and slugging percentage (SLUG) along with a method to calculate the number of runs created (RunsCreated). Finally, the structure contains an enumerated type used to specify the player's position.
Listing 3.3 Player Structure in VB .NET. Note that it looks much like a class.
Structure Player Public Sub New(ByVal playerName As String) Name = playerName End Sub Public Enum pPosition C = 2 P = 1 First = 3 Second = 4 Third = 5 Shortstop = 6 Left = 7 Center = 8 Right = 9 End Enum Public Name As String Public H As Integer Public D As Integer Public T As Integer Public HR As Integer Public BB As Integer Public AB As Integer Public SB As Integer Public Position As pPosition Public ReadOnly Property AVG() As Double Get Return H / AB End Get End Property Public ReadOnly Property SLUG() As Double Get Return (H + D + (2 * T) + (3 * HR)) / AB End Get End Property Public Function RunsCreated(ByVal useSB As Boolean) As Double If useSB Then Return ((H + BB) * (H + D + (2 * T) + (3 * HR) _ + (0.7 * SB))) / (AB + BB) Else Return ((H + BB) * (H + D + (2 * T) + (3 * HR))) / (AB + BB) End If End Function End Structure
A user of this structure might use the following code to represent the statistics from Sammy Sosa's 2000 season:
Dim sPlayer As Player With sPlayer .Name = "Sammy Sosa" .AB = 604 .H = 193 .D = 38 .T = 1 .HR = 50 .BB = 91 .SB = 7 .Position = Player.pPosition.Right End With Console.WriteLine(Math.Round(sPlayer.AVG, 3).ToString) Console.WriteLine(Math.Round(sPlayer.SLUG, 3).ToString) Console.WriteLine(Math.Round(sPlayer.RunsCreated(True), 1).ToString)
Because the structure contains a constructor (the New procedure), an alternate declaration would be
Dim sPlayer As New Player("Sammy Sosa")
Conversion
As mentioned in Chapter 1, one of goals of the CLR is to provide a type-safe runtime environment. This means that by default, the CLR will enforce type-safety at compile time by not allowing an object of one type to be assigned to an object of a different type. This behavior is much different from VB 6.0, where type coercion happened implicitly, and VB developers didn't have to worry about statements like the following:
Dim s as String s = 50
In this case, VB 6.0 implicitly converted the 50 to a string and made the assignment. In VB .NET, this same code will not compile unless the Option Strict statement is used and set to Off. This statement turns off the type-checking feature of the compiler, and then attempts to cast from one type to the other at runtime. In many instances, VB developers feel more comfortable turning off this option although it increases the likelihood of exceptions at runtime.
To perform explicit conversions, you can use the conversion functions (CStr, CBool, CDate, Cint, and so on), the CType and DirectCase functions, or methods of the System.Convert class. The CType function takes the expression to be converted and the type to convert to as parameters. For example, to convert the literal 50 to a string, you would use the following syntax:
s = CType(50, String)
The second argument can be used to explicitly pass a type, as in the preceding example, or you can use the GetType function to extract the type. You'll notice throughout the book that some listings have Option Strict set to Off for ease of coding and to make reading the code for VB developers more natural. The DirectCase function works in much the same way, although is more strict in that it only allows the conversion if the type parsed to it is exactly the same as the type being converted.
However, an easier approach is to use the System.Convert class, which contains seventeen To methods that convert to all of the system data types. In addition, each method supports myriads of overloads to convert from any of the types. Therefore, the previous code example could be rewritten:
s = Convert.ToString(50)
In addition, because all types are ultimately derived from System.Object, you can call the ToString method of any variable to convert it to a String. Calling ToString on a complex type, such as a structure or class, by default simply returns the type name as a string. However, if you define your own classes as described in Chapter 4, you can override the ToString method and return a string representation of your object.
Strings
Like arrays, strings in VB.NET are derived from a base class, in this case System.String, that contains both shared and instance members that allow you to manipulate the string. Table 3.6 shows the important members. Keep in mind that VB .NET also contains string functions in the Microsoft.VisualBasic.Strings module that wrap some of these methods or provide slightly altered functionality. Which technique you use is really a matter of personal preference. Existing VB developers will find the VB .NET functions more familiar, whereas developers new to the Microsoft product set, or coming from ASP, should use the Services Framework for the sake of uniformity among languages.
Table 3.6 String Class Members
Member |
Description |
---|---|
Clone |
Instance method that returns a new String with the same value |
Compare |
Shared method that compares two strings and returns an Integer specifying the result |
CompareOrdinal |
Shared method that compares two strings and returns an Integer specifying the result without taking into account the language or culture |
CompareTo |
Instance method that compares this instance with a given object |
Copy |
Shared method that creates a new instance of a String with the same value as the specified string |
CopyTo |
Instance method that copies a portion of the string to a character array |
Concat |
Shared method that creates a new String from one or more strings or objects |
Empty |
Shared constant representing an empty string |
EndsWith |
Instance method that determines whether a given string matches the end of this string |
Equals |
Both a shared and instance method that determines whether two strings have the same value |
Format |
Shared method used to format the string with the given format specification |
IndexOf |
Instance method that returns the index of the first occurrence of a string within this string |
Insert |
Instance method that inserts the given string at a given position within this string |
Join |
Shared method that concatenates a given separator between each element in a given array |
LastIndexOf |
Instance method that returns the last occurrence of a given string within this string |
PadLeft, PadRight |
Instance methods that align the current string with spaces or a specified character for a specified length |
Remove |
Instance method that deletes the specified number of characters from this instance of the string at the specified location |
Replace |
Instance method that replaces all occurrences of a specified string with the given string |
Split |
Instance method that splits the string into an array of strings based on a separator |
StartsWith |
Instance method that determines whether this string is prefixed with a given string |
Substring |
Instance method that retrieves a substring from the string |
ToCharArray |
Instance method that copies the characters of the string to a character array |
ToLower, ToUpper |
Instance methods that return a copy of the string in lower- or uppercase |
Trim, TrimEnd, TrimStart |
Instance methods that remove spaces or a set of characters from the string |
However, even though System.String is a reference type, the contents of a string are immutable, and all the methods that work on a string actually return a new instance of a string in the modified form. For example, to trim a string, the code
Dim s As String s = " Some spaces" s = Trim(s)
actually results in a new string being created and assigned the same variable s. Immutability also affects assignment statements, and so the code
Dim s As String Dim y As String s = "Hello" y = s s = "New value"
does not change the value of y because y is actually a different string.
Because working with strings in this way can be inefficient for large strings, the Services Framework also contains the StringBuilder class in the System.Text namespace. This class can be used to build strings that can be directly modified perhaps by removing, replacing, or inserting characters, without creating a new string with each modification. For example, if you were to write code that read multiple text files and appended the text from each file to a string, it would be more efficient to use a StringBuilder so that the string could be modified directly, rather than creating a new string with each iteration of the loop. An analogous example is the following code:
Imports System.Text Dim sbText As New StringBuilder() Dim i As Integer sbText.EnsureCapacity(600) For i = 1 To 50 sbText.Append("more...") Next
In this example, the StringBuilder object is instantiated, and its EnsureCapacity method is called to make sure that enough space can be allocated to hold the result. The loop then appends the text to the string. Remember that because the StringBuilder object is not actually a string, you need to use the ToString method to convert it to a string for other uses.
NOTE
The StringBuilder class also is used to allocate string buffers that are filled by unmanaged DLL functions, as is frequently the case with the Win32 API.
VB .NET also does not support fixed-length strings, and so the code that would allocate a 30-character fixed-length string in VB 6.0
Dim s As String * 30
does not compile in VB.NET. The length of a string in VB .NET is determined by its contents only.
Because a string derives from a base class, you would think that you might be able to use a constructor for a string as in
Dim s As New String("E.D. Cope")
However, the String class does not support a constructor that accepts a string. An array of System.Char can be passed to the constructor like so:
Dim arrChar() As Char arrChar = CType("O. Marsh", Char()) Dim strPaleo As New String(arrChar)
However, if you were going to do this, you would be better off using the initialization technique described earlier in the chapter because it would be more efficient. You would want to use the constructor when initializing a string with a repeated series of characters. The following code initializes a string to 50 spaces:
Dim s As New String(Convert.ToChar(" "), 50)
Finally, as shown in Table 3.6, the String class contains several versions of the Compare and Equals methods that you can use to perform both case-sensitive (the default) and case insensitive comparisons on strings. As in VB 6.0, string comparisons also can be done with the standard operators (=, <>, <, >, >=, <=) using either a binary or text comparison depending on the setting of Option Compare.
Block Scope
In previous versions of VB, variables could be declared at various scopes that included local (procedure), module, class, and global. Although VB .NET retains these scoping rules, it adds a lower level of scoping called the block level.
Block level scoping allows a developer to dimension a variable inside any block statement in VB .NET. These include looping constructs such as the For and While loops, If Then statements, and Try Catch statements, among others. If a variable is declared inside a block, the variables can be accessed only inside that block. For example, in the following code, the variable j is only accessible while in the If Then statement:
Dim i As Integer If i = 3 Then Dim j As Integer j = 2 End If j = 3 ` Causes a compiler error
In this case, the compiler stops you from referencing j outside the block. In addition, the compiler does not let you dimension a variable also called j with local scope. However, a j variable at the module or global level is allowed, although when executing the block, the block variable will be referenced, as you would expect.
Basically, the addition of block scope means that developers can more easily hide data and use it only where appropriate. It is good programming practice to declare variables at the lowest scope available and only raise them to a higher scope when absolutely necessary. This promotes reusability by creating fewer dependencies in your code.
NOTE
Variables in block scope are not deallocated when the block is exited. Block-scoped variables live for the entirety of the procedure they are declared in. When entering the same block several times in the same invocation of the procedure, you might need to initialize the variable.
Procedures
There are several subtle changes in the way VB .NET procedures and functions behave. The following list notes these changes:
Perhaps the most important is the change in the default parameter passing mechanism. In VB 6.0, if you did not specify how a parameter was to be passed, it was always passed by reference (ByRef). In VB .NET, this has changed to by-value (ByVal) because many developers didn't take the time to explicitly use the ByVal keyword. Passing parameters by-value protects them from changes inside the procedure, and therefore is safer.
In VB 6.0, if you passed the property of an object into a procedure using ByRef, the property was not changed as expected when the procedure ended. This has been corrected in VB .NET, where ByRef arguments work as expected with properties.
ParamArray parameters in VB 6.0 were always passed ByRef whereas in VB .NET they are passed ByVal, and the elements of the array must be declared as Object.
Arguments marked as Optional in VB 6.0 did not require a default value, whereas in VB .NET, default values are required. This requirement makes the IsMissing keyword obsolete, so it has been removed.
Returning data from functions also has changed in VB .NET with the inclusion of the Return keyword. This keyword can be used, rather than setting the return value equal to the name of the function. This makes the code both easier to read and write.
The way procedures can be called also has changed. In previous versions of VB, the capability to call a Sub procedure without using parentheses caused a good deal of confusion. In VB .NET, the Call keyword is retained although it is optional, but all procedures that accept arguments must be called with parentheses. If the procedure does not accept arguments, the parentheses are optional, although VS .NET includes them for you anyway. As a result the statement
TIP
Although ByVal is now the default, you still should specify it explicitly for readability. In fact, VS .NET does this automatically.
MsgBox "Done with process", vbOK, "Dinosaurs"
in VB.NET would now be
MsgBox("Done with process", MsgBoxStyle.OKOnly, "Dinosaurs")
At the procedure declaration level, new access modifiers can be used, covered in Chapter 4.
The GoSub statement is no longer supported. Although this has caused much discussion in the VB community, it is probably for the best because developers often can write code that is difficult to maintain with GoSub.
A procedure can no longer be marked as Static to automatically make all its local variables static (to retain their values between invocations of the procedure). Now, each variable that is to be static must be declared with the Static keyword.
Using the Compatibility Class
With the core language assembly, VB .NET ships with types that allow you to write code that is more compatible with VB 6.0. This includes classes, interfaces, structures, constants, and enumerated types that are used in VB 6.0.
Primarily, these include types that facilitate the communication between VB 6.0 controls, such as CheckBox and ListBox with classes in the Windows Forms namespace. In addition they include types that are useful for working with the ADO Data Control (ADOC) and implementing data binding. As a result they are not particularly interesting for devleopers of distributed applications.
That being the case, it is not recommended that you use these types in your applications.