Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

changes from VB6 to VB.Net

0 views
Skip to first unread message

emcgizmo

unread,
Dec 13, 2001, 3:15:10 AM12/13/01
to
VB.NET introduces major changes to the VB language. Some are
modifications to existing ways of working, whereas others are brand
new. This chapter will cover some of those changes, but this is by no
means an exhaustive list of all changes from VB to VB.NET. First,
you'll see some of the features that have changed. Then you will see
some of the new features.

General Changes
There are a number of general changes to be aware of when moving from
VB to VB.NET. Among them are topics such as the removal of default
properties, subs and functions requiring parentheses, ByVal being the
default method for passing parameters, and changes to the logical
operators. These changes, and others, are detailed in this section.

Default Properties
In VB6, objects could have default properties. For example, the
following code is perfectly valid in VB6, if you assume that Text1 is
a text box:

Text1="Hello, World"
This code takes the string "Hello, World" and sets the default
property of the text box, the Text property, to the string. The major
drawback to default properties is that they require you to have a Set
command in VB. For example, take a look at the following block of VB6
code:

Dim txtBillTo as TextBox
Dim txtShipTo as TextBox
txtShipTo = txtBillTo
The line of code txtShipTo = txtBillTo sets the Text property of
txtShipTo to the value in the Text property of txtBillTo. But what if
that isn't what you wanted? What if, instead, you wanted to create an
object reference in txtShipTo that referred to the object txtBillTo?
You'd have to use this code:

Set txtShipTo = txtBillTo
As you can see, default properties require you to use the Set keyword
to set references from one object variable to another.

VB.NET gets around this problem by getting rid of default properties.
Therefore, to copy the Text property from txtBillTo into the Text
property of txtShipTo, you'd have to use this code:

txtShipTo.Text = txtBillTo.Text
Setting the two variables equal to each other sets a reference from
one to the other. In other words, you can set an object reference
without the Set keyword:

txtShipTo = txtBillTo ' Object reference in VB.NET
To be more precise, default properties without parameters are no
longer supported. Default properties that require parameters are still
valid. Default properties with parameters are most common with
collection classes, such as in ADO. In an ADO example, if you assume
that rs is an ADO Recordset, check out the following code:

rs.Fields.Item(x).Value ' OK, fully qualified
rs.Fields(x).Value ' OK, because Item is parameterized
rs.Fields(x) ' Error, because Value is not parameterized
The easy solution is to fully qualify everything. This avoids any
confusion about which properties are parameterized and which are not.
However, as you know from your VB days, the number of dots you have
should be minimized. The reason is that each dot requires an OLE
lookup that slows you down. Therefore, you should code carefully when
dealing with parameters.

Subs and Functions Require Parentheses
As you saw in the last chapter when you used the MsgBox function, you
must now always use parentheses with functions, even if you are
ignoring the return value. In addition, you must use parentheses when
calling subs, which you did not do in VB6. For example, assume that
you have this sub in both VB6 and VB.NET:

Sub foo(ByVal Greeting As String)
' implementation code here
End Sub
In VB6, you could call this sub in one of two ways:

foo "Hello"
Call foo("Hello")
In VB.NET, you also could call this sub in one of two ways:

Foo("Hello")
Call foo("Hello")
The difference, of course, is that the parentheses are always required
in the VB.NET calls, even though you aren't returning anything. The
Call statement is still supported, but it is not really necessary.

Changes to Boolean Operators
The And, Not, and Or operators were to have undergone some changes.
Microsoft originally said that the operators would short-circuit, but
now they are staying the way they worked in VB6. This means that in
VB.NET, as in VB6, if you had two parts of an And statement and the
first failed, VB6 still examined the second part. Examine the
following code:

Dim x As Integer

Dim y As Integer
x = 1
y = 0
If x = 2 And y = 5/y Then
...
As a human, you know that the variable x is equal to 1. Therefore,
when you look at the first part of the If statement, you know that x
is not equal to 2, so you would logically think it should quit
evaluating the expression. However, VB.NET examines the second part of
the expression, so this code would cause a divide-by-zero error.

If you want short-circuiting, VB.NET has introduced a couple of new
operators: AndAlso and OrElse. In this case, the following code would
not generate an error in VB.NET:

Dim x As Integer
Dim y As Integer
x = 1
y = 0
If x = 2 AndAlso y = 5/y Then
...
This code does not cause an error; instead, because x is not equal to
2, VB.NET does not even examine the second condition.

Declaration Changes
You can now initialize your variables when you declare them. You could
not do this in VB6. In VB6, the only way to initialize a new variable
was to do so on a separate line, like this:

Dim x As Integer
x = 5
In VB.NET, you can rewrite this into one line of code:

Dim x As Integer = 5
Another significant, and much-requested, change is that of declaring
multiple variables, and what data type they assume, on one line. For
example, you might have the following line:

Dim x, y As Integer
As you're probably aware, in VB6, y would be an Integer data type, but
x would be a Variant. In VB.NET, this has changed, so both x and y are
Integers. If you think, "It's about time," there are many who agree.
This should remove a number of bugs and strange type conversions
experienced by new VB developers. It should also make the code more
efficient by making variables the expected type instead of using the
Object type (which replaces the Variant type, as you will see later).

Support for New Assignment Operators
VB.NET now supports shortcuts for performing certain assignment
operations. In VB6, you incremented x by 1 with the following line of
code:

x = x + 1
In VB.NET, you can type an equivalent statement like this:

x += 1
Not only can you use the plus sign, but VB.NET now also supports -=,
*=, /=, \=, and ^= from a mathematical standpoint, and &= for string
concatenation. If all this looks like C/C++, that's where it came
from. However, the ++ operator is not supported. Microsoft made a
decision not to include the ++ operator because they felt it made the
code more difficult to read.

Because VB.NET is in beta and has not yet been performance tuned, it
is unclear whether these new assignment operators will be more
efficient. These operators did tend to be more efficient in C/C++, due
to a more efficient use of the CPU's registers. Therefore, it will be
interesting to test these new operators when the final, tuned version
of VB.NET is released.

ByVal Is Now the Default
In what many consider a strange decision, the default way to pass
parameters in VB has always been by reference. The decision was
actually made because passing by reference is faster within the same
application, but can be costly if you are calling components across
process boundaries. If you're a little rusty, by reference means that
you are passing only the address of a variable into the called
routine. If the called routine modifies the variable, it actually just
updates the value in that memory location, and therefore the variable
in the calling routine also changes. Take this VB6 example:

Private Sub Command1_Click()
Dim x As Integer
x = 3
foo x
MsgBox x
End Sub

Sub foo(y As Integer)
y = 5
End Sub
If you run this example in VB6, the message box shows the value 5.
That happens because in VB6, when you pass x to foo, you are just
sending the memory address of the variable x, so when foo modifies y
to 5, it is changing the value in the same memory location to which x
points, and this causes the value of x to change as well.

If you tried to type this example into VB.NET, you'd see something
happen. First, of course, you'd have to add parentheses around x in
your call to foo. However, when you tried to type the definition of
foo, VB.NET would automatically add the word ByVal into the
definition, so it would end up looking like this:

Sub foo(ByVal y As Integer)
If you wanted to pass by reference, you would have to add the ByRef
keyword yourself, instead of VB.NET using the new default of ByVal.
This is a benefit to those of you calling procedures across process
boundaries, something that is common in the world of distributed
applications. In addition, this should cut down on errors like those
seen by novice users who didn't understand the concept of passing by
reference in previous versions of VB.

Block-Level Scope
VB.NET adds the ability to create variables that are visible only
within a block. A block is any section of code that ends with one of
the words End, Loop, or Next. This means that For...Next and If...End
If blocks can have their own variables. Take a look at the following
code:

While y < 5
Dim z As Integer
...
End While
The variable z is now visible only within the While loop. It is
important to realize that although z is visible only inside the While
loop, its lifetime is that of the procedure. That means if you
re-enter the While statement, z will have the same value that it did
when you left. Therefore, it is said that the scope of z is block
level, but its lifetime is procedure level.

While...Wend Becomes While...End While
The While loop is still supported, but the closing of the loop is now
End While instead of Wend. If you type Wend, the editor automatically
changes it to End While. This change finally move the While loop into
synch with most other VB "block" structures, which all end with an End
<block> syntax.

Procedure Changes
VB.NET has changes that affect how you define and work with
procedures. Some of those changes are mentioned in this section.

Optional Arguments Require a Default Value
In VB6, you could create an optional argument (or several optional
arguments) when you defined a procedure. You could, optionally, give
them a default value. That way, if someone chose not to pass in a
value for an argument, you had a value in it. If you did not set a
default value and the caller did not pass in a value, the only way you
had to check was to call the IsMissing statement.

IsMissing is no longer supported because VB.NET will not let you
create an optional argument that does not have a default value.
IsMissing is not needed because an optional argument is guaranteed to
have a value. For example, your declaration might look like this:

Sub foo(Optional ByVal y As Integer = 1)
Notice that the Optional keyword is shown, just as it was in VB6. This
means a parameter does not have to be passed in. However, if it is not
passed in, y is given the default value of 1. If the calling routine
does pass in a value, of course, y is set to whatever is passed in.

Static Not Supported on Subs or Functions
In VB6, you could put Static in the declaration of a sub or function.
Doing so made every variable in that sub or function static, which
meant that they retained their values between calls to the procedure.
For example, this was legal in VB6:

Static Sub foo()
Dim x As Integer
Dim y As Integer
x = x + 1
y = y + 2
End Sub
In this example, x will retain its value between calls. So, the second
time this procedure is called, x already has a value of 1, and the
value of x will be incremented to 2. The variable y would have the
value of 2, and therefore the second time in it would be incremented
to 4.

VB.NET does not support the Static keyword in front of the sub or
function declaration anymore, as you saw in the example above. In
fact, if you want an entire sub or function to be static, you need to
place Static in front of each variable for which you want to preserve
the value. This is the only way to preserve values in variables inside
a sub or function. In VB.NET, the equivalent sub would look like this:

Sub foo()
Static x As Integer
Static y As Integer
x = x + 1
y = y + 2
End Sub
The Return Statement
The Return statement can be used to produce an immediate return from a
called procedure, and can optionally return a value. For example, look
at this block of code:

Function foo() As Integer
While True
Dim x As Integer
x += 1
If x >= 5 Then
Return x
End If
End While
End Function
In this code, you enter what normally would be an infinite loop by
saying While True. Inside the loop, you declare an integer called x
and begin incrementing it. You check the value of x and when it
becomes greater than or equal to 5, you call the Return statement and
return the value of x. This causes an immediate return, and does not
wait to hit the End Function statement.

The old way of doing it still works, however. For example, to rewrite
this procedure using the "old" approach to return a value, you would
make it look like the following code. This sets the name of the
procedure, foo, to the value to be returned. Then you have to exit the
loop so that you'll hit End Function. You also could have used Exit
Function instead of Exit Do.

Function foo() As Integer

Do While True
Dim x As Integer
x += 1
If x >= 5 Then
foo = x
Exit Do
End If
Loop
End Function
ParamArrays Are Now Passed ByVal
A parameter array, or ParamArray, is used when you do not know how
many values you will pass into a procedure. A ParamArray allows for an
unlimited number of arguments. A parameter array automatically sizes
to hold the number of elements you are passing in.

A procedure can have only one ParamArray, and it must be the last
argument in the definition. The array must be one-dimensional and each
element must be the same data type. However, the default data type is
Object, which is what replaces the Variant data type in VB.NET.

In VB6, all the elements of a ParamArray are always passed ByRef. This
cannot be changed. In VB.NET, all the elements are passed ByVal. This
cannot be changed. Again, ByRef was used in previous versions of VB
because it was more efficient for passing parameters within a program
all running in the same application space. When working with
out-of-process components, however, it is more efficient to pass
parameters ByVal.

Properties Can Be Modified ByRef
In VB6, if a class had a property, you could pass that property to a
procedure as an argument. If you passed the property ByVal, of course,
it just copied the value. If you passed it ByRef, the called procedure
might modify the value, but this new value was not reflected back in
the object's property.

In VB.NET, however, you can pass a property to a procedure ByRef, and
any changes to the property in the called procedure will be reflected
in the value of the property for the object.

Take the following VB.NET example. Don't worry if the Class syntax
looks strange; just realize that you are creating a class called Test
with one property: Name. You instantiate the class in the
button1_Click event procedure, and pass the Name property, by
reference, to foo. foo modifies the variable and ends. You then use a
message box to print out the Name property.

Private Sub button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles button1.Click
Dim x As New Test()
foo(x.Name)
MsgBox(x.Name)
End Sub

Sub foo(ByRef firstName As String)
firstName = "Torrey"
End Sub

Public Class Test
Dim firstName As String
Property Name() As String
Get
Name = firstName
End Get
Set(ByVal Value As String)
firstName = Value
End Set
End Property
End Class
When you run this example, the message box will display "Torrey". This
shows that the property of the object is being passed by reference,
and that changes to the variable are reflected back in the object's
property. This is new in VB.NET.

Array Changes
Arrays have undergone some changes as well. Arrays could be somewhat
confusing in previous versions of VB. VB.NET seeks to address any
confusion by simplifying the rules and removing the capability to have
nonzero lower boundaries.

Array Size
In VB6, if you left the default for arrays to start at 0, declaring an
array actually gave you the upper boundary of the array, not the
number of elements. For example, examine the following code:

Dim y(2) As Integer
y(0) = 1
y(1) = 2
y(2) = 3
In this VB6 code, you declare that y is an array of type Integer, and
the upper boundary is 2. That means you actually have three elements:
0&#8211;2. You can verify this by setting those three elements in
code.

In VB.NET, array declaration was going to change, so that the
parameter was the number of elements. However, due to the possibility
of breaking existing code in the upgrade process, this has been
changed back to the way it worked in VB.

Lower Boundary Is Always Zero
VB6 allowed you to have a nonzero lower boundary in your arrays in a
couple of ways. First, you could declare an array to have a certain
range. If you wanted an array to start with 1, you declared it like
this:

Dim y(1 To 3) As Integer
This would create an array with three elements, indexed 1&#8211;3. If
you didn't like this method, you could use Option Base, which allowed
you to set the default lower boundary to either 0 (the default) or 1.

VB.NET removes those two options from you. You cannot use the 1 to x
syntax, and Option Base is no longer supported. In fact, because the
lower boundary of the array is always 0, the Lbound function is no
longer supported.

Array Assignment Is ByRef Instead of ByVal
In VB6, if you had two array variables and set one equal to the other,
you actually were creating a copy of the array in a ByVal copy. Now,
in VB.NET, setting one array variable to another is actually just
setting a reference to the array. To copy an array, you can use the
Clone method.

Option Strict
Option Strict is a new statement that specifically disallows any type
conversions that would result in data loss. You can use only widening
conversions; this means you could convert from an Integer to a Long,
but you could not convert from a Long to an Integer. Option Strict
also prevents conversions between numbers and strings.

Option Strict is on by default in Visual Studio.NET Beta 2. To turn it
off, go to the top of the code window and type Option Strict Off.

You can see that the IDE recognized potential problems with the
following lines of code:

Dim longNumber As Long
Dim integerNumber As Integer
integerNumber = longNumber
With this code, the IDE will underline longNumber and a tooltip will
tell you the following: Option Strict disallows implicit conversions
from Long to Integer.

Data Type Changes
There are several changes to data types that are important to point
out. These changes can have an impact on the performance and resource
utilization of your code. The data types in VB.NET correspond to the
data types in the System namespace, which is important for
cross-language interoperability.

All Variables Are Objects
Technically, in VB.NET, all variables are subclassed from the Object
base class. This means that you can treat all variables as objects.
For example, to find the length of a string, you could use the
following code:

Dim x As String
x = "Hello, World"
MsgBox(x.Length)
This means that you are treating x as an object, and examining its
Length property. Other variables have other properties or methods. For
example, an Integer has a ToString method that you can use, which you
will see in a moment.

Short, Integer, and Long
In VB.NET, the Short data type is a 16-bit integer, the Integer is a
32-bit integer, and the Long is a 64-bit integer. In VB6 code, you
might have used Long in many places because it was 32-bit and
therefore performed better on 32-bit operating systems than Integer,
which was just 16-bit. On 32-bit operating systems, 32-bit integers
perform better than 64-bit integers, so in VB.NET, you will most
likely want to return to using the Integer data type instead of the
Long data type.

Automatic String/Numeric Conversion Not Supported
In VB6, it was easy to convert from numbers to strings and vice versa.
For example, examine this block of code:

Dim x As Integer
Dim y As String
x = 5
y = x
In VB6, there is nothing wrong with this code. VB will take the value
5 and automatically convert it into the string "5". VB.NET, however,
disallows this type of conversion by default. Instead, you would have
to use the CStr function to convert a number to a string, or the Val
function to convert a string to a number. You could rewrite the
preceding code for VB.NET in this manner:

Dim x As Integer
Dim y As String
x = 5
y = CStr(x)
y = x.ToString ' This is equivalent to the previous line
Fixed-Length Strings Not Supported
In VB6, you could declare a fixed-length string by using a declaration
like the one shown here:

Dim y As String * 30
This declared y to be a fixed-length string that could hold 30
characters. If you try this same code in VB.NET, you will get an
error. All strings in VB.NET are variable length.

All Strings Are Unicode
If you got tired of worrying about passing strings from VB to certain
API calls that accepted either ANSI or Unicode strings, you will be
happy to hear that all strings in VB.NET are Unicode.

The Value of True
Long ago, someone at Microsoft decided that True in VB was equal to
&#8211;1. The .NET Framework declares that True should be equal to 1.

In reality, 0 is False and any nonzero is True. However, if you ask
for the value of True, you will get &#8211;1 inside of VB.NET. You can
verify that any nonzero is True by using this code:

Dim x As Integer

x = 2
If CBool(x) = True Then
MsgBox("Value is True")
End If
If you make x any positive or negative integer, the CBool function
will make it True. If you set x equal to 0, CBool will return False.
Even though True is actually shown as &#8211;1 in VB.NET, if you pass
it out to any other .NET language, it is seen as 1.

The important thing to understand is this: Do not code for the value
(1 or &#8211;1). Just use the keyword True and you will avoid any
problems.

The Currency Data Type Has Been Replaced
The Currency data type has been replaced by the Decimal data type. The
Decimal data type is a 12-byte signed integer that can have up to 28
digits to the right of the decimal place. It supports a much higher
precision than the Currency data type, and has been designed for
applications that cannot tolerate rounding errors. The Decimal data
type has a direct equivalent in the .NET Framework, which is important
for cross-language interoperability.

The Variant Data Type Has Been Replaced
The Variant data type has been replaced. Before you start thinking
that there is no longer a catch-all variable type, understand that the
Variant has been replaced by the Object data type. The Object data
type takes up only four bytes because all it holds is a memory
address. Therefore, even if you set the variable to an integer, the
Object variable holds four bytes that point to the memory used to
store the integer. The integer is stored on the heap. It will take up
8 bytes plus 4 bytes for the integer, so it consumes a total of 12
bytes. Examine the following code:

Dim x
x = 5
In this case, x is a variable of type Object. When you set x equal to
5, VB.NET stores the value 5 in another memory location and x stores
that memory address. When you attempt to access x, a lookup is
required to find that memory address and retrieve the value.
Therefore, just like the Variant, the Object data type is slower than
using explicit data types.

According to the VB.NET documentation, the Object data type allows you
to play fast-and-loose with data type conversion. For example, look at
the following code:

Dim x
x = "5" ' x is a string
x -= 2
In this example, x will now hold the numeric value 3. The only way you
can run this example is if you set Option Strict Off in the file. If
you change the code to use string concatenation, the conversion also
works fine. For example, the following code will run without an error,
and x will end up holding the string "52":

Dim x
x = "5" ' x is a string
x &= 2
An Object data type can be Nothing. In other words, it can hold
nothing, which means it doesn't point to any memory address. You can
check for Nothing by setting the Object variable equal to Nothing or
by using the IsNothing function. Both checks are shown in the
following code:

Dim x
If x = Nothing Then msgbox("Nothing")
If IsNothing(x) Then msgbox("Still nothing")
Dim x As Object
If x Is Nothing Then MsgBox("Nothing")
If IsNothing(x) Then MsgBox("Still Nothing")
Structured Error Handling
Error handling has changed in VB.NET. Actually, the old syntax still
works, but there is a new error handling structure called
Try...Catch...Finally that removes the need to use the old On Error
Goto structure.

The overall structure of the Try...Catch...Finally syntax is to put
the code that might cause an error in the Try portion, and then catch
the error. Inside the Catch portion, you handle the error. The Finally
portion runs code that happens after the Catch statements are done,
regardless of whether or not there was an error. Here is a simple
example:

Dim x, y As Integer ' Both will be integers
Try
x \= y ' cause division by zero
Catch ex As Exception
msgbox(ex.Message)
End Try
NOTE

The ex As Exception part of the Catch statement is optional.

Here, you have two variables that are both integers. You attempted to
divide x by y, but because y has not been initialized, it defaults to
0. That division by zero raises an error, and you catch it in the next
line. The variable ex is of type Exception, which holds the error that
just occurred, so you simply print the Message property, much like you
printed Err.Description in VB6.

In fact, you can still use Err.Description, and the Err object in
general. The Err object will pick up any exceptions that are thrown.
For example, assume that your logic dictates that an error must be
raised if someone's account balance falls too low, and another error
is raised if the balance drops into the negative category. Examine the
following code:

Try
If bal < 0 Then
Throw New Exception("Balance is negative!")
ElseIf bal > 0 And bal <= 10000 Then
Throw New Exception("Balance is low; charge interest")
End If
Catch
MessageBox.Show("Error: " & Err().Description)
Finally
MessageBox.Show("Executing finally block.")
End Try
In this case, your business logic says that if the balance drops below
zero, you raise an error informing the user that the balance is below
zero. If the balance drops below 10,000 but remains above zero, you
notify the user to start charging interest. In this case,
Err().Description picks up the description you threw in your
Exception.

You can also have multiple Catch statements to catch various errors.
To have one Catch statement catch a particular error, you add a When
clause to that Catch. Examine this code:

Try
x \= y ' cause division by zero
Catch ex As Exception When Err().Number = 11
MsgBox("You tried to divide by zero")
Catch ex As Exception
MsgBox("Acts as catch-all")
End Try
In this example, you are looking for an Err().Number of 11, which is
the error for division by zero. Therefore, if you get a
division-by-zero error, you display a message box that says You tried
to divide by zero. However, if a different error were to occur, you
would drop into the Catch without a When clause. In effect, the Catch
without a When clause acts as an "else" or "otherwise" section to
handle anything that was not handled before.

Notice that the code does not fall through all the exceptions; it
stops on the first one it finds. In the preceding example, you will
raise an Err().Number of 11. You will see the message You tried to
divide by zero, but you will skip over the catch-all Catch. If you
want to run some code at the end, regardless of which error occurred
(or, indeed, if no error occurred), you could add a Finally statement.
The code to do so follows:

Try
x \= y ' cause division by zero
Catch ex As Exception When Err().Number = 11
MsgBox("You tried to divide by zero")
Catch ex As Exception
MsgBox("Acts as catch-all")
Finally
MsgBox("Running finally code")
End Try
In this code, whether or not you hit an error, the code in the Finally
section will execute.

There is also a documented way to exit from a Try statement early. The
Exit Try keyword is supposed to break you out of the Try early,
ignoring any Finally code. The syntax would look like this:

Try
x \= y ' cause division by zero
Catch ex As Exception When Err.Number = 11
msgbox("You tried to divide by zero")
Exit Try
Catch ex As Exception
msgbox("Acts as catch-all")
Finally
msgbox("Running finally code")
End Try
Structures Replace UDTs
User-defined types, or UDTs, were a way to create a custom data type
in VB6. You could create a new data type that contained other elements
within it. For example, you could create a Customer data type using
this syntax:

Private Type Customer
Name As String
Income As Currency
End Type
You could then use that UDT in your application, with code like this:

Dim buyer As Customer
buyer.Name = "Martha"
buyer.Income = 20000
MsgBox(buyer.Name & " " & buyer.Income)
The Type statement is no longer supported in VB.NET. It has been
replaced by the Structure statement. The Structure statement has some
major changes, but to re-create the UDT shown earlier, the syntax is
this:

Structure Customer
Dim Name As String
Dim Income As Decimal
End Structure
Notice that the only real difference so far is that you have to Dim
each variable inside the structure, which is something you did not
have to do in the Type statement, even with Option Explicit turned on.
Notice also that Dim is the same as Public here, meaning that the
variables are visible to any instance of the structure.

Structures have many other features, however. One of the biggest
differences is that structures can support methods. For example, you
could add a Shipping method to the Customer structure to calculate
shipping costs based on delivery zone. This code adds a DeliveryZone
property and a DeliveryCost function:

Structure Customer
Dim Name As String
Dim Income As Decimal
Dim DeliveryZone As Integer

Function DeliveryCost() As Decimal
If DeliveryZone > 3 Then
Return 25
Else
Return CDec(12.5)
End If
End Function
End Structure
Here, you have a built-in function called DeliveryCost. To use this
structure, your client code would look something like this:

Dim buyer As Customer
buyer.Name = "Martha"
buyer.Income = 20000
buyer.DeliveryZone = 4
msgbox(buyer.DeliveryCost)
In this case, the message box would report a value of 25.

If you think this looks like a class, you are correct. In fact,
structures can have properties, methods, and events. They also support
implementing interfaces and they can handle events. There are some
caveats, however. Some of those stipulations include the following:

You cannot inherit from a structure.

You cannot initialize the fields inside a structure. For example, this
code is illegal:

Structure Customer
Dim Name As String = "Martha"
End Structure
Properties are public by default, instead of private as they are in
classes.

Structures are value types rather than reference types. That means if
you assign a structure variable to another structure variable, you get
a copy of the structure and not a reference to the original structure.
The following code shows that a copy is occurring because an update to
seller does not update buyer:

Dim buyer As Customer
Dim seller ' Object data type
seller = buyer
seller.Name = "Linda"
MsgBox(buyer.Name)
MsgBox(seller.Name)
Note that the preceding code will not work if you have Option Strict
turned on. If you want to test this, you'll have to enter Option
Strict Off.

IDE Changes
There are numerous IDE changes. Several of those have been addressed
already: the lack of the Line and Shape controls, the new menu
builder, the new way of setting the tab order, and the Dynamic Help
window. There are many other changes, such as the debugging windows,
but they will not be discussed in this small book.

New Items
In addition to changes to the core language and some of the tools,
there are some completely new features to VB.NET. The major new items
include such features as constructors and destructors, namespaces,
inheritance, overloading, free threading, and garbage collection. This
is not an exhaustive list by any means, but these six features are
worth discussing to one degree or another. One feature that should be
mentioned as well is something you've seen before: auto-indention. As
soon as you create a block, such as a Sub or If block, the IDE indents
the next line of code automatically.

Constructors and Destructors
Constructors and destructors are potentially new concepts for VB
programmers. They are similar to the Class Initialize and Class
Terminate events you have had with classes. At their simplest level,
constructors and destructors are procedures that control the
initialization and destruction of objects, respectively. The
procedures Sub New and Sub Destruct replace the VB6 Class_Initialize
and Class_Terminate methods. Unlike Class_Initialize, Sub New runs
only once, when the object is first created. Sub New cannot be called
explicitly except in rare circumstances.

Sub Destruct is called by the system when the object is set to Nothing
or all references to the object are dropped. However, you cannot
ensure when Sub Destruct actually will be called, thanks to the way
VB.NET does garbage collection, which is discussed at the end of this
chapter.

One of the reasons for constructors is that you can create an object
and pass in some initialization parameters. For example, assume that
you want to create a class dealing with a training course, and you
want to initialize the class with a course number so that it can
retrieve certain information from a database. In your class, you
create a Sub New method that accepts an argument for the course ID.
The first line inside the Sub New is a call to another constructor,
usually the constructor of the base class on which this class is
based. Fortunately, VB.NET gives you an easy way to call the
constructor of the base class for your current class: MyBase.New.

If you want to create a class for a training course and initialize it
with a course ID, your class would look something like this:

Class Course
Sub New(ByVal CourseID As Integer)
MyBase.New()
FindCourseInfo(CourseID)
...
End Sub
End Class
To call this class from the client code, your call would look
something like this:

Dim TrainingCourse as Course = New Course(5491)
Namespaces
Perhaps one of the most confusing aspects of VB.NET for VB developers
is the concept of a namespace. A namespace is a simple way to organize
the objects in an assembly. When you have many objects, such as in the
System namespace provided by the runtime, a namespace can simplify
access because it is organized in a hierarchical structure, and
related objects appear grouped under the same node.

For example, say you wanted to model the objects, properties, and
methods for a pet store. You might create the PetStore namespace. You
could then create some subnamespaces. For example, you might sell live
animals and supplies as two major categories. Within the live animals
category, you might sell dogs, cats, and fish. If this sounds like an
object model, it can be thought of as similar. However, none of these
are actual objects. You could have a namespace called
PetStore.Animals.Dogs, and in this namespace you'd find the classes
and methods necessary for dealing with dogs. If you wanted to handle
the inventory, you might find that in PetStore.Supplies. If you wanted
to look at the kinds of dog food you have in stock, you might look in
PetStore.Supplies.Dogs. Where the physical classes exist is up to you;
they all can be in one big assembly, but logically separated into
these various namespaces.

If you want to use the objects in an assembly without having to fully
qualify them each time, use the Imports statement. Doing so allows you
to use the names of the objects without fully qualifying their entire
namespace hierarchy.

For example, when you created your first VB.NET project in Chapter 2,
"Your First VB.NET Application," you saw some Imports statements at
the top of the form's code module. One of those statements was Imports
System.WinForms. That statement made all the classes, interfaces,
structures, delegates, and enumerations available to you without you
having to qualify the full path. That means the following code is
legal:

Dim x as Button
Without the Imports System.WinForms statement, your line of code would
have to look like this:

Dim x as System.WinForms.Button
In fact, the MsgBox that you know and love can be replaced by the
System.WinForms.MessageBox class. MsgBox still works for
compatibility, but it is actually part of the
Microsoft.VisualBasic.Interaction namespace, which contains a number
of methods that approximate functions in previous versions of VB.

If you're wondering why Microsoft pulled out some language elements
and made them part of the runtime, it should be fairly obvious: so
that any language targeting the runtime, on any platform, would have
access to common functionality. That means that you can go into C# and
have a message box, just by importing System.WinForms and calling the
MessageBox class. MessageBox is quite powerful&#8212;the Show method
has twelve variations of it.

Creating Your Own Namespaces
You are free to create your, own namespaces inside your assemblies.
You can do this simply by inserting your own Namespace...End Namespace
block. Inside the namespace block, you can have structures, classes,
enums, interfaces, and other elements. You must name the namespace and
it becomes what someone would import. Your code might look like this:

Namespace VolantTraining
Public Class Customer
'code here
End Class

Public Class Student
'code here
End Class
End Namespace
Namespaces can be nested within other namespaces. For example, your
namespace might look something like this:

Namespace VolantTraining
Namespace Customer
Class Training
...
End Class
Class Consulting
...
End Class
End Namespace
Namespace Student
...
End Namespace
End Namespace
Here you have one namespace, VolantTraining, that holds two other
namespaces: Customer and Student. The VolantTraining.Customer
namespace holds the Training and Consulting classes, so you could have
customers who have used your training services and customers who have
used your consulting services.

If you choose not to create explicit namespaces, all your classes and
modules still belong to a namespace. This namespace is the default
namespace and is the name of your project. You can see this namespace,
and change it if you want, by clicking viewing the Project Properties
dialog box for your project. The text box labeled Root Namespace
represents the root namespace for your project. If you declare a
namespace within your project, it is subordinate to this root
namespace. Therefore, if the root namespace of your applications
Project1, then the full namespace for VolantTraining would be
Project1.VolantTraining.

Inheritance
The most-requested feature in VB for years has been inheritance.
Microsoft often countered that VB did inheritance; VB did interface
inheritance, which meant that you could inherit (what VB called
implement) an interface. However, the interface you implemented did
not have any code in it, or if it did, the code was ignored.
Therefore, the class implementing the interface had to provide methods
for all the methods in the interface, and the code had to be rewritten
in each class that implemented the interface. VB developers wanted to
be able to write that code once, in a base class, and then to inherit
that class in other classes, which would then be called derived
classes. The derived classes should be able to use the existing code
in the base class. With VB.NET, developers have their wish.

Not only does your derived class inherit the properties and methods of
the base class, it can extend the methods and, of course, create new
methods (in the derived class only). Derived classes can also override
any existing method in the base class with a new method of the same
name, in a process called overriding. Forms, which are really just
classes, can be inherited to create new forms.

There are many concepts to inheritance, and seeing it in practice is
important enough to make it a chapter unto itself. Chapter 5,
"Inheritance with VB.NET," is all about inheritance and how to use it
in VB.NET.

Overloading
Overloading is another feature that some VB developers have been
requesting for a long time. In short, overloading allows you to define
the same procedure multiple times. The procedure has the same name but
a different set of arguments each time.

You could fake this in an ugly way in VB6. You could pass in an
argument as a Variant, and then use the VarType command to check the
type of variable that was passed in. This was cumbersome, and the code
could get nasty if your procedure had to accept an array or a
collection.

VB.NET gives you a nice way to handle this issue. Imagine that you
have a procedure that can accept a string or an integer. The
functionality inside the procedure would be quite different depending
on whether what is passed is a string or an integer. Your code might
look something like this:

Overloads Function FindCust(ByVal psName As String) As String
' search name field for %psName%
End Function

Overloads Function FindCust(ByVal piCustNo As Integer) As String
' search CustID field
End Function
You now have a function called FindCust that can be passed either an
integer or a string. Your calls to it could look like this:

Dim x As String
x = FindCust("Hello")
x = FindCust(1)
As long as Option Strict is turned on (which is the default, at least
in Beta 2), you cannot compile an invalid call. For example, there is
no overloaded FindCust that accepts a floating-point value of any
kind. VB.NET would not let the following code compile:

x = FindCust(12.5)
Free Threading
For the first time, VB.NET has given VB developers the ability to
write truly free-threaded applications. If your application is going
to perform a task that could take a long time, such as parsing through
a large recordset or performing a complex series of mathematical
calculations, you can push that processing off to its own thread so
that the rest of your application is still accessible. In VB6, , the
best you could do to keep the rest of the application from appearing
to be locked was to use the DoEvents method.

Examine this code, which is written for VB.NET. Here you have some
code for button4. This code calls the BeBusy routine, which has a loop
in it to just to take up time. However, while in this loop, you are
consuming the thread for this application, and the UI will not respond
while the loop is running.

CAUTION

This takes about eight seconds to run on my machine. It might run a
significantly longer or shorter time on your machine. The good news is
the VB.NET IDE runs on a separate thread, so if you find yourself
waiting forever, just click on the IDE and choose Stop Debugging from
the Debug menu.

Private Sub button4_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles button4.Click
BeBusy()
End Sub

Sub BeBusy()
Dim i As Decimal
For i = 1 To 10000000
'do nothing but tie up app
Next
Beep()
End Sub
To create a thread, you must use the System.Threading.Thread class. In
the creation of the class, you pass in the name of the procedure or
method you want to run on that thread. You preface the procedure or
method name with the AddressOf operator. Your code would look like
this:

Dim busyThread As New System.Threading.Thread(AddressOf BeBusy)
To fix the code and keep BeBusy from consuming the main program
thread, you have now created a new thread and will run BeBusy on that
thread. However, that line of code isn't enough. Next, you must call
the Start method on that new thread. With VB.NET, calling BeBusy on
its own thread would look like this:

Private Sub button4_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles button4.Click
Dim busyThread As New System.Threading.Thread(AddressOf BeBusy)
busyThread.Start()
End Sub
No changes are required to the BeBusy procedure. If you now run this
code, the interface will remain active while BeBusy is running. The
Beep in BeBusy will let you know when the procedure has finished
running.

There are some caveats to using free threading. They seem significant,
but each one has a fairly reasonable workaround. Some of those caveats
and workarounds are as follows:

The procedure or method you run on a new thread cannot have any
arguments. To get around this problem, you have a couple of choices.
You could use global variables, but that is not an elegant solution.
Instead, create properties or fields in the class whose method you are
calling, and set those properties after the object is created.
Obviously, that wouldn't help you in the earlier example because the
call was simply to a sub in the same program.

The procedure or method you run on a new thread cannot return a value.
To get around that issue, you could use global variables, but again,
this is not an elegant solution. One major problem is that your
application would have to keep checking the thread to see when it was
done, before you would be safe in using that global variable. Instead,
you should consider raising an event with the return value as a
parameter in the event.

Synchronization is also an issue with free-threaded applications. If
you are performing a complex series of calculations on one thread,
other parts of your application must wait until that thread is
finished before they can use the results. You can monitor threads to
see when they are finished or you can have the methods on those
threads raise events to notify you when they are done. VB.NET provides
an IsAlive property for each thread, so you can check to see when the
thread is running.

There is much more to free threading, but it is not a topic that will
be covered in more detail in this book.

Garbage Collection
Garbage collection is now being handled by the runtime. In VB6, if you
set an object to Nothing, it was destroyed immediately. This is no
longer true in VB.NET. Instead, when you set an object to Nothing or
it loses all its references, it becomes garbage collectable. It's
still in memory, taking up resources. The garbage collector runs on a
separate thread, and it passes by occasionally looking for objects to
clean up (destroy). This lag is often less than a second, but it could
be longer on a system in heavy use. Even though the object has been
marked for garbage collection, any resources it has opened are still
open, including any data or file locks that it might have obtained.

Microsoft calls this "no deterministic finalization" because the
developer is no longer truly in control of when the object will be
destroyed. The Sub Dispose is called when the object is truly
destroyed by the garbage collector. You can call the garbage collector
by using the Collect method of the GC class in the System namespace.

Summary
There are a number of changes to the VB language in the move to
VB.NET. It is important to understand them as you move forward. Some
changes are minor, such as the requirement to use parentheses and the
lack of default properties. More significant changes on the list are
features such as the new Try...Catch...Finally error handling.

Finally, there are some critical new features. Namespaces are the most
dramatic change that will affect every VB.NET developer. Inheritance
and free threading will also make major changes in the way
applications are written.

Now that you have seen many of the significant changes in the language
itself, each of the next chapters covers a single topic, showing
either new functionality (such as Windows Services and Web Services)
or changes to how things are done (such as building classes and
assemblies).

0 new messages