Sunteți pe pagina 1din 277

1.

Getting Started
In this book, we work under the basic assumption that you have no inkling about any of the
programming languages, and VisualBasic.Net in particular. But, you would invariably have
heard people fantasizing about the great new product called Visual Studio.Net, which is in the
reckoning to bring about a revolution in the software industry.

So, let us get to the meat of this product. We shall gain a meticulous understanding of this
language and its features, and only then shall we decide whether the claims made by the
populace are justified or not. We could keep extolling the virtues of the finer things in life, but
we'd rather get down to brass-tacks straightaway. We assume that you have a copy of Visual
Studio.Net installed on your machine.

To launch Visual Studio, click on the ubiquitous Start Button in Windows. (In our case, we
have installed Windows 2000). Then, click on Programs, followed by Microsoft Visual Studio
.Net. Finally, select the option of Microsoft Visual Studio .Net. This is shown in the Screen 1.1.

Screen 1.1
Shortly thereafter, a window pops up, showing Visual Studio.Net in all its glory, as shown in
screen 1.2.
Screen 1.2
Hey, hang on! Could there be something amiss with what you are doing? Your copy of Visual
Studio .Net could be displaying something totally different!! Yes, this could be a distinct
possibility!

One of the salient features of present day software is that it can be customized to a large
extent. This is why the final appearance of a product could be drastically different from what it
originally looked like, when freshly taken out of the box. Thus, the screen 1.2 will never look
identical on all the machines. We shall explicate these differences in due course, but for the
moment, we are exceedingly inclined towards embarking upon the development of a small
application or project. But before venturing any further, close all the child windows by clicking
on the x sign.

Then, click on the menu option File, followed by New, and finally Project. This becomes evident
in screen 1.3.
Screen 1.3
The above actions will result in the display of the Dialog box titled "New Project". This is shown
in screen 1.4.

The advantage of using Visual Studio .Net is that diverse applications, each using a different
programming language, can be developed simultaneous, under a single streamer.

This explains why three language options are present in the first pane of the dialog box, viz.
Visual Basic, Visual C# and Visual C++.

Screen 1.4
The option of Visual Basic project is listed first. Hence, it is highlighted by default. This
signifies the priorities of Microsoft, and is the very raison de etré of this book!
A perfect analogy for Visual Studio .Net is that of a glass, which can be used to drink either
water or juice or wine. In a similar fashion, under the single roof of Visual Studio .Net, various
programs developed in different languages, can be written. The language used is simply not an
issue.

The second pane titled 'Templates' determines the type of application that would eventually be
created. This could be an executable file running under Windows, or a Web Application
running a web server.

Since we are naïve about this product, we select the option of Windows Application. This option
is selected by default.

The next task is to specify a name for the project. We have chosen the name 'vvv', since we
sincerely believe that it will bring us good tidings on our very first application. We have used
our newly created folder called 'v1' as the location for creating the application. You are at
liberty to choose any folder that you fancy.

Screen 1.5
After specifying all the details as shown in screen 1.5, click on the OK button.

This brings us to Screen 1.6, where we encounter an empty form.


Screen 1.6
Now, in order to view the output of our handiwork, click on the menu option Debug, and then
on Start. This is shown in screen 1.7.

Screen 1.7
This ignites the excitement of Visual Studio .Net, and it starts processing the request. Finally,
as the outcome of the operation, it displays an empty form, as seen in screen 1.8. This is no
mean achievement for people like us, considering the fact that we have not written even a
single line of code yet!
Screen 1.8
Close this form window by clicking on the x sign on the right hand side. This will revert us
back to the form in Visual Studio .Net. The next task that we venture upon is, to display a
button on the form. To accomplish this, click on the menu option View, and then, on the option
of ToolBox, as shown in screen 1.9.

Screen 1.9
This option brings up a toolbox containing a list of objects or controls. The toolbox window is
visible on the left hand side of the screen, as revealed in screen 1.10.
Screen 1.10
Now, simply click on the control labelled as Button to select it, and then, drag and drop it into
the form. Screen 1.11 displays the position where we have dropped our button.

Screen 1.11
Then, run the application in the customary manner by clicking on the menu Debug, and then,
click on Start. Screen 1.12 confirms that the form now contains the button in it. Pat on the
back! Having covered just a couple of pages, we have already placed a button on the form.
Screen 1.12
The shortcoming of our present endeavour is that, we are clueless about what is going on
behind the scenes. Clicking on the OK button in the New Project dialog box, results in the
generation of considerable amount of code by the Visual Studio .Net framework. Apparently, we
have merely clicked on the ToolBox, which is a User Interface tool; and then, we have dragged
and dropped a button in the form. However, in reality, ample code has been generated in the
background to accomplish this seemingly simple task.

We are convinced that prior to forging ahead with the task of building complex applications
using Visual Studio .Net, there is a need for discerning the code that it generates. Also, along
the way, we shall keep reverting back to the Visual Studio .Net framework, to demonstrate as
to how it has made a programmer's life more easygoing. However, you have to learn a
programming language that is to be used with it, since it is a fundamental fact that Visual
Studio .Net cannot build customized applications by itself. It is the task of the programmer to
program it to satisfy the specific requirements.

So, we start by writing the smallest Visual Basic program. As is customary in all our books, we
shall refrain from attempting to impress you with our knowledge of programming. Instead, we
shall proceed systematically, a single step at a time. So, here we go!

First, create a folder named il on your hard disk. Then, using a text editor such as Notepad,
create a file called a.vb, with the following contents in it:

a.vb
Module zzz
End Module

To execute the above code, go to the Visual Studio .NET Command Prompt (Start-> Program->
Visual Studio .Net-> Visual Studio .Net Tools-> Visual Studio .NET Command Prompt), and run
a program called vbc. We achieve it as follows:

c:\il>vbc a.vb

This action of ours results in the following error message:


vbc : error BC30420: 'Sub Main' was not found in 'a'.

Every Visual Basic program must be enclosed within the words 'Module' and 'End Module'.
There is no rational explanation for this prerequisite. So, we accept it as the dictum. Every
Module requires a name. So, we have chosen the name 'zzz'. This name has been deliberately
chosen to demonstrate that the name per se, is not very significant. Any name would suffice, as
long as one is present. An empty module is forbidden. Further, the error message displayed
earlier clearly mandates the presence of a function or a sub-routine called Main.

a.vb
Module zzz
Sub Main()
End Sub
End Module

After we insert two lines within the Module, the compiler successfully compiles without any
hiccups. In the directory, you will see a file called a.exe, which when executed, does not display
anything.

Visual Basic is a case-insensitive language. However, we still follow the Microsoft convention of
writing the word Module with a capital M, even though we are at liberty to determine our own
capitalization rules.

After having attended to the first rule of Visual Basic, i.e. enclosing the code within the words
'Module' and 'End Module', we progress onto the second rule, which insists on the presence of
a sub-routine called Main. The word 'sub' is a short form for the term sub-routine, which takes
a name along with it. It is merely a collection of code.

A sub-routine is created in a manner similar to the creation of a module, where we begin with
the word Sub, followed by the name of the sub-routine, i.e. Main. Finally, the subroutine has to
conclude with the word 'End', followed by the word 'Sub'. A 'Sub' can have a pair of open and
close brackets. However, they are optional. So, on odd days we write 'Sub' with brackets in our
programs, while on even days, we omit them ;-).

The Visual Basic language is verbose and rather lax in its rules. Now, let us write a program
that displays a string.

a.vb
Module zzz
Sub Main()
System.Console.WriteLine ("Hello")
End Sub
End Module

Output
Hello

On compiling and executing the above program, we are greeted by the word "Hello" in the
output. It has been an ancient programming tradition wherein, the first program in any
programming language displays the word "Hello". The word "Hello" gets displayed on the screen
because a function has been executed or called.

In a programming language, a function name constitutes a word, followed by a pair of round


brackets.
Thus, Main is a function, since it ends with a pair of round brackets. However, there is a subtle
difference, which is:
• In the case of Main, we were creating a function or a sub-routine, since the line started
with the word 'Sub'.
• In the case of System.Console.WriteLine ("Hello"), the word 'Sub' is not present. Hence,
it construes that a function or subroutine is being called for execution.

There is a minor distinction between a subroutine and a function, which we shall attend to
later on, in the chapter.

The name of our function i.e. System.WriteLine.Console, is considerably long. We need not
torment ourselves about the details of how this function has been written, at this stage. We
only have to be conscious of the fact that it requires some words to be inserted within double
quotes, which it will display on the screen.

Since we have specified the word Hello within double quotes, the console displays the word
"Hello". This is the mechanism of passing data or parameters to a function. This function is one
of the innumerable ones that are available in Visual Basic. We can use them on multiple
occasions in our code.

a.vb
Module zzz
Sub Main
System.Console.WriteLine ("Hello")
System.Console.WriteLine (100)
End Sub
End Module

Output
Hello
100
A single function is capable of embodying multiple lines of code. In the above program, we have
called the function System.Console.WriteLine twice:
• On the first occasion, we have passed a string or a sequence of letters of the alphabet as
a parameter.
• On the second occasion, we have passed it a number.

The function dutifully prints out both the values without complaining at all.

a.vb
Module zzz
Sub Main
System.Console.WriteLine (vijay)
End Sub
End Module

Error
c:\il\a.vb(3) : error BC30451: Name 'vijay' is not declared.
System.Console.WriteLine (vijay)

In the WriteLine function, we have specified the word vijay, without enclosing it within inverted
commas. The Visual Basic product or compiler reverts back with an error, expressing its
ignorance about the name vijay. It expects it to be declared explicitly.
a.vb
Module zzz
Sub Main
dim vijay
vijay = 100
System.Console.WriteLine (vijay)
End Sub
End Module

Output
100

To create or declare any valid name, the line must begin with the word DIM, followed by the
name, thereby, informing VisualBasic.Netthat a name is being created. Thus, 'DIM vijay'
signifies that the name vijay is being declared. These names are created so that they can store
values in them. On the next line, we have specified a value of 100 to be stored in the name
vijay.

An elementary rule in all programming languages is that, a number or a string may be


substituted by a name that holds the value. Therefore, when the function WriteLine is
furnished with the name vijay, no error is generated. This is because, the framework first
identifies the value contained in it. Since vijay holds the value of 100, it is displayed on the
screen.

a.vb
Module zzz
Sub Main
dim vijay
vijay = 100
System.Console.WriteLine (vijay)
vijay = 200
System.Console.WriteLine (vijay)
vijay = vijay + 10
System.Console.WriteLine (vijay)
vijay = vijay + 1
System.Console.WriteLine (vijay)
End Sub
End Module

Output
100
200
210
211

As was done in the program above, we create a name vijay and assign it a value of 100. Then,
using the WriteLine function, the value is displayed. vijay is then re-assigned a new value of
200, which is also displayed. Thus, the value stored in vijay can vary. Due to this ability to
hold varying values, words such as vijay, are termed as variables.

On the next line, we have performed an arithmetic operation by presenting the statement vijay
= vijay + 10. On encountering a line containing an 'equal to' sign =, initially all attention is
riveted on the expression that is on the right side of the sign. This expression is 'vijay + 10'. So
firstly, the value of vijay is evaluated to obtain a value of 200. Then, 10 is added to it, in order
to obtain a result of 210.

This value is now assigned to the variable on the left hand side of the 'equal to' sign, i.e. vijay,
thereby updating its value. Thus, when the variable vijay is displayed, it shows the value of
210. In the next round, the value of the variable is increased by 1, thus, making the final value
as 211.

The concept of variables is one of the pillars on which the programming world rests. Here, a
word or name holds a value, which can keep fluctuating.

a.vb
Module zzz
Sub Main
dim vijay
vijay = 100
System.Console.WriteLine (vijay)
vijay = "hi"
System.Console.WriteLine (vijay)
End Sub
End Module

Output
100
hi

In this example, the variable vijay is first assigned a numeric value 100. Then, it is assigned a
string value of "hi". Nobody complains and both values get displayed. Thus, we have the license
to change the data type of the value that the variable can hold.

a.vb
Module zzz
Sub Main
dim vijay as integer
vijay = 100
System.Console.WriteLine (vijay)
vijay = "hi"
System.Console.WriteLine (vijay)
End Sub
End Module

Output
100

Unhandled Exception: System.InvalidCastException: Cast from string "hi" to type 'Integer' is not valid. --->
System.FormatException: Input string was not in a correct format.
at Microsoft.VisualBasic.CompilerServices.DoubleType.Parse(String Value, NumberFormatInfo
NumberFormat)
at Microsoft.VisualBasic.CompilerServices.IntegerType.FromString(String Value)
--- End of inner exception stack trace ---
at Microsoft.VisualBasic.CompilerServices.IntegerType.FromString(String Value)
at zzz.Main()
The previous program does not demonstrate the conventional technique of programming,
because a variable must essentially represent only a single type during its lifetime. To maintain
absolute consistency, the data type is specified while creating the variable.

This is achieved by adding the word 'as' after the DIM variable, followed by a predefined data
type. vijay is assigned the data type of integer, which implies that the variable vijay shall store
only numbers or integers.

After displaying the number that it contains, the variable vijay is then initialized to a string
value of "hi". The earlier program had not protested under similar circumstances.
However, now we come across a mammoth dialog box displaying an error message. When we
click on No, we observe the above error on the console.

This error or exception enucleates the fact that, we are not empowered to specify a string value
for a variable that has been declared to be of type integer. Henceforth, we shall declare all our
variables with a data type.

a.vb
Module zzz
Sub Main
dim i,j as integer
i = 10
System.Console.WriteLine ("{0} {1}", i, j)
End Sub
End Module

Output
10 0

In a single stroke, we have the ability to create as many variables as we wish, separating each
one of them with a comma. The above example has two integer variables named i and j, which
have been declared together. The variable i is assigned a value of 10, while j is not initialized at
all. In the same manner that was used to create two variables simultaneously, we now wish to
print their values too in a single stroke.

To accomplish this, the WriteLine function has been modified and assigned a string that has
the numbers 0 and 1 enclosed within curly braces. This string parameter is followed by the two
variables i and j, separated by commas. They are extra parameters.

The WriteLine function replaces the first curly brace of 0, with the value of the first parameter
after the string i.e. i, and the second curly braces of 1 with the second parameter after the
string, i.e. j. Since the value of the variable i is 10, and the value of the variable j is 0, the
output that is displayed is 10 0. Thus, not only have we learnt how to print multiple variables
using a single WriteLine function, but have also become wiser by the fact that, if a variable is
not assigned a value, it obtains a default value of 0.

a.vb
Module zzz
Sub Main
dim b as boolean
dim i as integer
dim j as string
j = "hi"
i = 10
b = true
System.Console.WriteLine ("{0} {1} {2} {3}" , i > 20 , i < 20 , b , j = "hi")
End Sub
End Module

Output
False True True True

A data type represents a specific type of data. In the above example, we have created three
types of variables:
• i is of type integer
• j is of type string
• b is of type Boolean.

A Boolean type can store only one of the two values, true or false.

The variable i of type integer can hold any valid number. It is assigned the value of 10.

The variable j, of type string, must have its value enclosed within double inverted commas. It is
assigned a value of "hi".

The boolean variable b is initialized to a value of true.

In the WriteLine function following the string, we first display the expression i > 20, instead of
the variables. Doing so, indirectly poses the question "Is the variable i greater than 20?"
The result can either be true or false. Since the value of the variable i is 10, which is less than
20, the value of False is displayed. The second expression determines whether the value of i is
less than 20 or otherwise. Since this is affirmative, the value of True is displayed on the screen.

The third parameter to the WriteLine function simply displays the value of the boolean variable
b. The value of the boolean variable is not altered from what it was initialized to. Hence, the
output is displayed as True.

The last check performed is, whether the value of the string j is equal to the string value "hi" or
not. Since they both are the same, the resultant value of True is displayed. Thus, all the above
expressions result in a boolean value of either true or false.

a.vb
Module zzz
Sub Main
dim i as integer
i = 10
if i > 50 then
System.Console.WriteLine ("Condition True")
End if
if i > 5 then
System.Console.WriteLine ("Second Condition True")
End if
End Sub
End Module

Output
Second Condition True
The programs that we have presented so far are absolutely straightforward, since every line of
code that is entered, gets called sequentially. We desire to execute code based on certain
circumstances.

The above program is the perfect prescription for this. We have created an integer i with a value
of 10. Then, we encounter an 'if' statement.

An 'if' statement is always followed by a condition or an expression, and the line ends with a
'then'. In the first case, a check is performed to determine whether the value of the variable i is
larger than 50 or not. Since the answer is False, all the lines of code leading up to the 'End If',
are ignored. Thus, the first WriteLine function with the Condition True, does not get executed
at all.

The condition with the second 'if' statement, verifies whether the value of i is greater than 5 or
not. Since it is, all the code between the 'if' and the 'end if' is executed. Thus, an 'if' statement
is used to make decisions, i.e. to intelligently execute specific code, depending upon whether
some variables have certain values or not. In this manner, we can effortlessly write intelligent
and dynamic programs.

a.vb
Module zzz
Sub Main
dim i as integer
i = 10
if i > 50 then
System.Console.WriteLine ("Condition True")
else
System.Console.WriteLine ("Condition False")
End if
if i > 5 then
System.Console.WriteLine ("Second Condition True")
End if
End Sub
End Module

Output
Condition False
Second Condition True

In the above program, the 'if' statement now has the 'else' clause added to it. In the earlier
program, when the result of the 'if' statement was false, no code was executed.
Now, since the 'else' clause has been added, whenever the 'if' statement is false, the statements
within the 'else' block are executed. So, all code from the 'else' upto the 'end if', is executed.
When the 'if' condition results in True, the statements contained within the 'if' upto the 'else',
are executed.

The advantage of using the 'else' clause is that, some code finally gets executed. However, the
'else' clause is optional.

Subroutines

a.vb
Module zzz
Sub Main
abc
End Sub
End Module

Error
c:\il\a.vb(3) : error BC30451: Name 'abc' is not declared.

By introducing the name abc, we have attempted to call a subroutine or a function by that
name. Like before, an error is generated, since abc has not been declared. Thus, as in the case
of variables, we have to declare or create the subroutine or function before calling our own
code.

a.vb
Module zzz
Sub Main
abc
End Sub
sub abc
System.Console.WriteLine("in abc")
end sub
End Module

Output
in abc

At the very outset, we had created a subroutine called Main, by using Sub. Harnessing the
same mechanism, we now create another subroutine called abc.

The only difference between the two sub-routines is that the code in Sub Main is called when
the .exe file is executed, whereas, the code in the other subroutines has to be called explicitly.

To execute code in a subroutine, just the name of the subroutine has to be specified on a blank
line. Hence, we have merely specified the name abc. This action calls the code in the
subroutine, thereby resulting in the WriteLine function being called, with the string 'in abc'.
This sounds too easy to be true!

a.vb
Module zzz
Sub Main
abc
pqr
End Sub
sub abc
System.Console.WriteLine("in abc")
pqr()
pqr
end sub
sub pqr
System.Console.WriteLine("in pqr")
end sub
End Module

Output
in abc
in pqr
in pqr
in pqr

We can create as many subroutines or subs as we like in a single module. The above program
displays two subroutines, both of which are called from the Main block. Furthermore, the pqr
subroutine is called twice from the abc subroutine.
Thus, we enjoy the privilege of calling code contained in any other subroutines, as many times
as we desire. You would notice that at one stage we had called the pqr subroutine, using the
open and close brackets. The brackets are optional. So, their presence or the lack of it, does
not interfere much with the execution of the code.

a.vb
Module zzz
Sub Main
abc("Hi")
End Sub
sub abc()
System.Console.WriteLine("hell")
end sub
End Module

c:\il\a.vb(3) : error BC30057: Too many arguments to 'Public Sub abc()'.

In the above example, an error is generated, since we are passing the string parameter "Hi" to
the sub abc; whereas, the sub abc has not been declared to accept any parameters. So, let us
rectify this situation in the subsequent example.

a.vb
Module zzz
Sub Main
abc("Hi")
End Sub
sub abc(s as string)
System.Console.WriteLine(s)
end sub
End Module

Output
Hi

The above example demonstrates the correct way of creating a subroutine. The string "hi" is
passed as the value for the variable 's' in the sub abc. The variable 's' is termed as a parameter.
It is supplied to the WriteLine function for display purposes. Subroutines that accept
parameters are very practical and efficacious, since they infuse a lot of flexibility and generality
into the program.

a.vb
Module zzz
Sub Main
Dim s,t,u as string
s = "hi"
t = "bye"
u = "No"
abc(s,t,u)
System.Console.WriteLine("{0} {1} {2}",s,t,u)
End Sub
sub abc(s0 as string , byval s1 as string, byref t1 as string)
System.Console.WriteLine("{0} {1} {2}",s0,s1,t1)
s0 = "good"
s1 = "bad"
t1 = "ugly"
end sub
End Module

Output
hi bye No
hi bye ugly

In the above example, there are three string variables viz. s, t and u. They are initialized to "Hi",
"Bye" and "No", respectively. These three variables are then furnished as parameters to the
subroutine abc.

In the Sub, the values passed by the three variables, are stored in the parameters s0, s1 and
t1. We will explore the other parameter details in a short while.

Thereafter, the three values are displayed using the WriteLine function. Then, the values of the
parameters s0, s1 and t1 are modified to some other values. Once the subroutine abc ends, the
control moves on to the next line in the Main block.

Here, we again use the WriteLine function to display the values of the variables s, t and u. To
our utter astonishment, we discover that the value of the variable 'u' is different from what it
had been initialized to. It displays the new value that has been assigned to it in the abc
subroutine. However, s and t retain their old values.

In sub abc, we have changed the values of the parameters, and not the original variables.
Notwithstanding this, the value contained in the variable 'u' within the main subroutine,
changes. This is because of the fact that, we have affixed the word 'byref' before the name of
the parameter. This word signifies that if the parameter t1 changes in the sub abc, the original
variable in the calling Sub would also change correspondingly.

By using the word 'byval', any changes made to the value of the variable in the subroutine,
would not percolate down to the original variable.

The default is 'byval', and since it has not been mentioned for the first parameter, the effect is
that of byval. Thus, it is the sub that decides on whether to reflect the changes made, onto the
original variable that is passed as a parameter.

a.vb
Module zzz
Sub Main
Dim s as string
s = "hi"
abc()
End Sub
sub abc()
s = "No"
end sub
End Module

Error
c:\il\a.vb(8) : error BC30451: Name 's' is not declared.
We did not enlighten you earlier about a very essential feature of Sub. However, this seems to
be an opportune time to divulge the fact that, a Sub is an independent and a self-contained
entity. The variable 's' created in the sub main, is not discernible in the sub abc. Hence, the
compiler generates the above error.

a.vb
Module zzz
Sub Main
Dim s as integer
s = abc(10,20)
System.Console.WriteLine(s)
End Sub
Function abc(i as integer , j as integer) as integer
abc = i + j
end Function
End Module

Output
30

Your reaction to this book could range from being extremely positive to being outright negative
and unimpressed. This would depend upon how well you relate to our style. It has always been
our endeavour to explain only a single concept at a time. This could get exasperating for a large
number of people.

The above example highlights the difference between a sub and a function. They are almost
identical, but for one variation, i.e. a function can return a value, whereas, a Sub cannot.

In many situations, returning values is very important because functions normally perform
certain tasks and return the result of their operation. The above function abc accepts two
integers parameters and returns their sum.

Creating functions is a child's play, since it entails replacement of the word 'Sub' with the word
'Function' and the words 'End Sub' with the words 'End Function'. The words 'as Integer'
signify that the function will return an integer when it is called. To return a value in a function,
the name of the function has to be initialized to the specific value. Thus, writing abc=100 will
return 100.
In our case, we are adding up the two parameters 10 and 20, and initializing the function
name abc with the sum of 30. Thus, the variable s in the sub main will now have a value of 30,
which is the return value of the Function.

a.vb
Module zzz
Sub Main
Dim s as integer
s = abc(10,20)
abc(100,300)
System.Console.WriteLine(s)
End Sub
Function abc(i as integer , j as integer) as integer
abc = 300
abc = i + j
end Function
End Module
Output
30

You may set the return value as many times as you like. No one would whine. The last value to
be returned will be the one that is finally used. Thus, the return value of 300 is overridden by
the last value to be returned, i.e. 30.

Even though it is the job of a function to return a value, we can avoid storing the return value
in a variable. Thus, in the second round, when the function abc is called, we do not save its
return value in a variable. However, nobody makes a fuss about it.

As has been oft repeated by us, VisualBasic.Netis a very forgiving and flexible language, and
that is precisely why we love it as much as we do!
2. Classes
Visual Basic has never been considered to be a clean programming language. This became a
Gordian knot for Microsoft and began brewing trouble. However, in spite of this setback,
amazingly, no other product has sold more copies than Visual Basic in the history of the
computing world. Thus, Microsoft has no choice but to retain the heart and soul of Visual
Basic, and simultaneously, embellish it with the latest features available in language
technology. This forms the genesis of Visual Basic.Net. Visual Basic .Net is primarily an
extension of the Visual Basic programming language that is shipped with the Visual Basic
product.

In Visual Basic.Net, which is the improved version of Visual Basic, we can replace the word
'module' with the word 'Class'. Together with this modification, one more change is required,
i.e. the word 'Shared' has to be added to the Sub Main. If the main subroutine is not amended
to 'shared', the following error crops up:

Error
vbc : error BC30737: No accessible 'Main' method with an appropriate signature was found in 'a'.

a.vb
Class zzz
Shared Sub Main
System.Console.WriteLine("hi")
End Sub
End Class

Output
hi

Now, when we compile and run the program, the code in Main gets called, displaying the word
"hi". A class is an entity like a module, which carries code and variables.

a.vb
Class zzz
Shared Sub Main
abc
End Sub
Shared sub abc
System.Console.WriteLine("in abc")
end sub
End Class

Output
in abc

The above example has a subroutine called abc, with the word 'Shared' added to it. If the word
'Shared' is omitted, the compiler generates the following error:

Error
c:\il\a.vb(3) : error BC30369: Cannot refer to an instance member of a class from within a shared
method or shared member initializer without an explicit instance of the class.

Let us now delve upon what the word 'Shared' signifies.

a.vb
Class zzz
Shared Sub Main
dim a as zzz
a = new zzz
a.abc
pqr
zzz.pqr
End Sub
sub new()
System.Console.WriteLine("in new")
end sub
sub abc
System.Console.WriteLine("in abc")
end sub
shared sub pqr
System.Console.WriteLine("in pqr")
end sub
End Class

Output
in new
in abc
in pqr
in pqr

The above program has Main and pqr subroutines marked as 'shared', whereas, the others
remain unmarked.

In Sub Main, we first need to create a variable named 'a' of type 'zzz'. Here, we are not actually
creating an object or variable 'a' of type zzz, but merely stating that the variable 'a' looks like
the class zzz. This is akin to creating variables of a certain type. Also, we can safely presume
that integer, string and long are all classes. The difference is that the Visual Basic.Net compiler
is aware of the fact that these classes are data types.

zzz is a user-defined datatype, which the compiler is oblivious to. Hence, we need to create an
actual instance or occurrence of this datatype. By using the keyword 'new', an instance or an
object of type zzz is created. The line a = new zzz creates a new object zzz in memory, and
returns a handle that is stored in 'a'. To frame it differently, 'a' represents a fresh zzz object in
memory.

An object is an instance of a class. Thus, the statement 'DIM i as integer' creates an object i,
which is an instance of class integer. For the basic in-built data types such as string, boolean
and integer, there is no need to explicitly mention 'new', which is otherwise mandatory for the
user-defined types.

In Visual Basic.Net, the keyword 'new' first allocates memory for all the class contents, and
then, it checks for a sub called 'New'. If the sub 'new' exists, it executes the code present in it.
In most other languages, the sub new() is equivalent to a constructor of a class. After the
subroutine ends, a zzz object is created and stored in 'a'. The object 'a' can then be used to call
the other members in the class.

Initially, the abc procedure is called and then, the sub pqr is called twice:
• At the first instance, without the use of the prefix 'a'.
• At the second instance, using the class prefix.

The word 'shared' implies that the function can be called without creating an object. Since abc
is not shared, we have to create an instance of a zzz object, in order to access it. The pqr sub is
marked as 'shared'. Therefore, it does not require an instance of an object to access it.

We normally preface a member with the name of the class, which is the default, if not specified
expressly. The sub of main also is marked as 'shared'. Therefore, it does not require an
instance of the class zzz before it can be used.

Prior to venturing further into the concept of Shared, let us anatomize the concept of a
constructor.

a.vb
Class zzz
Shared Sub Main
dim a as zzz
a = new zzz
End Sub
sub new(i as integer)
System.Console.WriteLine("in new {0}", i)
end sub
End Class

Error
c:\il\a.vb(4) : error BC30455: Argument not specified for parameter 'i' of 'Public Sub New(i As
Integer)'.

Earlier, the constructor did not accept any parameters. Thus, we were able to call it without
any parameters. In the above program, we have a constructor that expects one parameter of
type integer. However, in the process of creating an instance of the object zzz, we have not
furnished any parameters to 'new'. This results in an error.

a.vb
Class zzz
Shared Sub Main
dim a as zzz
a = new zzz(10)
End Sub
sub new(i as integer)
System.Console.WriteLine("in new {0}", i)
end sub
End Class

Output
in new 10

We have now redressed the above error by passing a parameter to the 'new' statement. So,
when the constructor gets called, the value received in the parameter i is 10. This value is
displayed on the Console. A constructor can be equipped to accept as many parameters as we
wish to supply it.

a.vb
Class zzz
Shared Sub Main
dim a as zzz
a = new zzz(10)
a.new(10)
End Sub
sub new(i as integer)
System.Console.WriteLine("in new {0}", i)
end sub
End Class

Error
c:\il\a.vb(5) : error BC30282: Constructor call is valid only as the first statement in an instance
constructor.

Unlike a subroutine, a new constructor cannot be called explicitly. An error is generated in the
above program, since we are attempting to call a constructor by itself. Thus, a constructor gets
called automatically only when the object is created, and not otherwise. Further, it cannot be
called manually.

a.vb
Class zzz
Shared Sub Main
dim a,b as zzz
a = new zzz(10)
b = new zzz()
End Sub
sub new(i as integer)
System.Console.WriteLine("in new {0}", i)
end sub
sub new()
System.Console.WriteLine("in new ")
end sub
End Class

Output
in new 10
in new
We can have innumerable constructors or 'new' statements in the program, provided each is
assigned different parameters. The above program has two new subroutines, the first one
accepts a single integer parameter, while the second one does not take any parameters. This
program is perfectly valid.

The concept of using the same function name with different parameters is called 'function
overloading' or 'sub overloading'.

Now, we present another example on 'function overloading'.

a.vb
Class zzz
Shared Sub Main
abc()
abc(10)
abc("hi")
End Sub
shared sub abc(i as integer)
System.Console.WriteLine("in abc {0}", i)
end sub
shared sub abc()
System.Console.WriteLine("in abc ")
end sub
shared sub abc(k as string)
System.Console.WriteLine("in abc {0}", k)
end sub
End Class

Output
in abc
in abc 10
in abc hi

In the above example, we have three subs with the same name 'abc', but each of them has
different parameters. Out of the three, two subs have the same number of parameters, but
their data types are distinct.

This is permitted, since the name of a function in Visual Basic.Net does not consist of the name
alone, but is composed of the name and the data type of each parameter. Thus, the name of
the first sub abc that accepts an integer parameter, could be represented by 'abci', where i is
the data type of the parameter. The last sub could be represented by 'abcs', where s stands for
the string type of the parameter. This naming concept initiates uniqueness in the function
names.

This concept is known as 'name mangling', where the signature of the function depends upon
both, the name of the parameters and their datatypes.

Now, we present yet another example on constructors to make the concept of constructors
crystal clear.

a.vb
Class zzz
Shared Sub Main
dim a as zzz
a = new zzz()
End Sub
End Class

a.vb
Class zzz
Shared Sub Main
dim a,b as zzz
b = new zzz()
End Sub
sub new(i as integer)
System.Console.WriteLine("in new {0}", i)
end sub
End Class

Error
c:\il\a.vb(4) : error BC30455: Argument not specified for parameter 'i' of 'Public Sub New(i As
Integer)'.

Let us comprehend the concept of constructors by examining the above two examples. The first
example compiles without any errors, despite containing no constructor code.

Visual Basic.Net is not complaining for the reason that, when the program does not contain
any constructors, the complier introduces one of its own, as follows:

Sub new
End sub

Thus, when a constructor is not created explicitly, the compiler creates one for us. This
constructor does not accept any parameters, nor does it do anything. However, if we include
even a single constructor, this complimentary constructor is carted-off or taken away. Thus, in
any situation, there would always be at least one constructor present in the code.

In the second example, a constructor with one parameter is present. Therefore, the free
constructor with no parameters, which would have otherwise been generated is not present.
So, creating an instance of the zzz object by calling the constructor that accepts no parameters,
generate an error.

To summarize, when no constructors are present in the class, a constructor that does not
accept any parameters, is automatically generated. However, with the insertion of even a single
constructor, the free constructor is not created. In such a situation, each and every new
variant of the constructor has to be added manually.

a.vb
Class zzz
dim i as integer
Shared Sub Main
dim a as zzz
a = new zzz
a.i = 10
a.abc
a.pqr
End Sub
Sub abc
System.Console.WriteLine(i)
i = 20
End Sub
Sub pqr
System.Console.WriteLine(i)
End Sub
End Class

Output
10
20

The above example contains an instance variable i. It is referred to as an instance variable


since unlike 'a', it is not created in a sub, but is created outside the subs and within the class.

The lifetime of an object such as 'a' endures as long as the sub is active; once the sub
concludes executing its code, all the variables that have been declared in it, lose their values or
are considered non-existent thereafter. The visibility of such variables is restricted to the
function or sub that they are created in. Thus, the object 'a' is visible only within the sub Main
and not in the other subs, such as abc.

On the other hand, the lifetime of the variable i is linked to the life-span of the program. Since
it is declared outside all the subs, it is visible in all the subs within the class.

In main, the value of i is set to 10. It is prefaced with the name of the object, since it is not
shared. Then, the sub abc is summoned, which displays the value of 10 and changes it to 20.
Next, when the subroutine pqr is called, the value of the instance variable is 20. This is for the
reason that, only a single copy of i is created in memory, when the zzz object is created. Thus,
both the subs, viz. abc and pqr refer to this same instance variable i.

a.vb
Class zzz
dim j as integer
shared dim k as integer
Shared Sub Main
dim a,b as zzz
a = new zzz(10)
a.abc(100)
b = new zzz(20)
b.abc(200)
End Sub
sub new(i as integer)
System.Console.WriteLine("in new {0} {1} {2}", i , j , k)
j=i
k=i
System.Console.WriteLine("in new {0} {1} {2}", i , j , k)
end sub
sub abc(p as integer)
System.Console.WriteLine("in abc {0} {1}", j , k)
k=p
j=p
end sub
End Class

Output
in new 10 0 0
in new 10 10 10
in abc 10 10
in new 20 0 100
in new 20 20 20
in abc 20 20

The above example reinforces the dissimilarities between a shared variable and a non-shared
variable. The words 'variable' and 'object' may be used interchangeably. In the manner akin,
what is pertinent to a sub is also applicable to a function, unless otherwise specified.

In the above program, the class zzz has two variables:


• j - which is an instance variable.
• k - which is a shared variable.

The basic distinction between the two is as follows:


(a) A shared variable belongs to a class, which implies that each time a new instance of the
class is created, the shared variable does not get re-created. To access the shared variable,
the name of the class is used, since there is only a single shared variable per class.

(b) An instance variable gets created, each time that a new instance of a class is created.
Therefore, if an object is instantiated on 20 counts, it will result in the creation of 20 new
copies of the instance variable in memory.

In Main, two local variables a and b, of type zzz are created. Since they are local, they have a
very short life span. While creating the object 'a', the constructor for the object zzz is assigned a
value of 10. Therefore, in the constructor, the value of i becomes 10, whereas, both j and k
have a value of 0. If the instance variables are not explicitly initialised, the system initialises
them to a value of zero.

The constructor assigns the value contained in i, i.e. 10, to both the variables j and k.
Therefore, in the second round, the WriteLine function displays the values of all three variables
as 10. The sub abc is then called with a parameter of 100. Here, the values of variables j and k
remain 10, as has been displayed by the WriteLine function. We now re-initialize both the
variables j and k to 100, and then, quit out of the sub.

The next statement in the Main function creates a new instance of zzz, wherein the constructor
is assigned a value of 20. The WriteLine function prints the values of i, j and k as 20, 0 and
100, respectively. The instance variable j has a value of 0, as it has been freshly created.
Shared variables are not affected by the 'new' keyword. Thus, the variable k retains its original
value of 100, which was set in the abc function. The second WriteLine function in the
constructor displays the newly assigned value of j and k, i.e. 20.

The abc function is called once again using the object b. The variable j now displays the value
of 20, since the constructor of the 'b' object in zzz had initialized it to 20. This variable is then
assigned a value of 200. However, the second copy of j, which belongs to the object 'b', retains
its value of 20, since it has nothing to do with the variable j from the object 'a'.

To summarize what we have been harping on over and over, an instance variable belongs to an
object, whereas, a shared variable belongs to a class. This is indubitably evident from the
manner in which these variables are accessed. We are taking the trouble of explaining all the
above code and concepts, in order that you can discern even the most diminutive of the Visual
Basic .Net programs, generated by Visual Studio.Net.
The approach that we have pursued is that we first explain the concepts using the Visual
Basic.Net compiler, and then we implement them in Visual Studio .Net.

Namespaces

a.vb
Class zzz
Shared Sub Main
dim a as yyy
a = new yyy
a.abc
End Sub
End Class
Class yyy
sub abc
System.Console.WriteLine("abc")
end sub
end class

Output
abc

The above example lucidly establishes that a Visual Basic.Net program can contain
innumerable classes, as long as one of these classes contains the Sub of Main. The above
program has two classes named zzz and yyy. In the class zzz, 'a' is declared to be an object of
type yyy. After having created the object, the function abc is called from it.
You may wonder as to what is the big deal here, since the program runs in a manner akin to
any other program. Although we accede to the fact that the program is no different from the
earlier ones, nonetheless, we would still like to forge ahead with it.

The class Console has a shared function called WriteLine, which is used to display strings. So,
nothing holds us back from creating a class called Console, in the fashion similar to the one
used to create the class yyy. Then, in the class, we can create our own function named
WriteLine.

This is perfectly within the realms of possibility, since we now know how to create a class and a
function within the class. However, there is bound to be some confusion because when the
user enters the statement 'Console.WriteLine', the framework would be in a dilemma whether
to call the function created by us or the one supplied by Microsoft.

Microsoft seems to have already foreseen this predicament, and therefore, to avert such name
clashes from occurring, the big guys or Visual Basic.Net ordains that every class should be a
part of a name or a namespace. The next example demonstrates the implementation of
namespaces.

a.vb
Class zzz
Shared Sub Main
dim a as nnn.yyy
a = new nnn.yyy
a.abc
End Sub
End Class
Namespace nnn
Class yyy
sub abc
System.Console.WriteLine("abc")
end sub
end class
End Namespace

Output
abc

We have effected a minor amendment to the class yyy. A new word, i.e. Namespace has been
introduced above the class yyy, and the name assigned to it is 'nnn'. As a rule, everything in
Visual Basic.Net must terminate with the word End. Therefore, the Namespace also ends with
the words 'End Namespace'.

With the introduction of a namespace, the name of the class is no longer just yyy, but is now
'nnn.yyy'. The syntax for the class name now changes to the following:
• name of the namespace followed by a dot, and finally, followed by the class name.

Thus, whenever we need to refer to the class yyy, the reference to be employed is nnn.yyy. If we
omit the namespace 'nnn' from the DIM statement, the following error is generated:

Error
c:\il\a.vb(3) : error BC30002: Type 'yyy' is not defined.

a.vb
Class zzz
Shared Sub Main
dim a as aaa.nnn.yyy
a = new aaa.nnn.yyy
a.abc
End Sub
End Class
Namespace aaa
Namespace nnn
Class yyy
sub abc
System.Console.WriteLine("abc")
end sub
end class
End Namespace
End Namespace

Output
abc

In the above example, two namespaces have been added to the class yyy, viz. aaa and nnn.
Thus, the name of the class now becomes aaa.nnn.yyy. The namespace statement can be re-
written as 'Namespace aaa.nnn'.

a.vb
Class zzz
Shared Sub Main
aaa.nnn.yyy.abc
End Sub
End Class
Namespace aaa.nnn
Class yyy
shared sub abc
System.Console.WriteLine("abc")
end sub
end class
End Namespace

Output
abc

In this example, the abc function is marked as 'shared'. Here, no object of type yyy has been
created. Thus, to call the abc function, the name of the class 'yyy.abc' is used. Now, with the
inclusion of the namespace tag above the class, the only way to call this function is by using its
full name, i.e. aaa.nnn.yyy.abc.

This syntax bears a resemblance to the commonly used System.Console.WriteLine. Thus, a sub
or a function has to be read from right to left as follows:
• The rightmost entity 'WriteLine' or 'abc' is the name of the method.
• The next entry on the left, i.e. 'Console' or 'yyy' is the name of the class.
• The next entry on the left, i.e. 'System' or 'nnn' is the name of the namespace entity.
The Console class belongs to the System namespace. The WriteLine function is a shared
function in this class. In the case of methods that are not 'shared', the object name has to be
expressly specified to access the method.

Namespaces influence only the class names and not the object names. By employing
namespaces, both similar as well as disparate classes can be placed under a single heading.
Microsoft has politely reserved the use of the 'System' namespace. Thus, no developer can ever
create a namespace by that name, thereby avoiding any clashes with Microsoft's set of entities.

Classes sharing the same names can be placed under different namespaces without any
confusion. However, the problem with using these namespaces is that they are extremely
lengthy. The next program provides a solution to this poser.

a.vb
Imports aaa.nnn
Class zzz
Shared Sub Main
dim a as yyy
a = new yyy
a.abc
End Sub
End Class
Namespace aaa.nnn
Class yyy
sub abc
System.Console.WriteLine("abc")
end sub
end class
End Namespace

Output
abc
The program begins with the newly introduced keyword of 'Imports', followed by the name of
the namespace, i.e. aaa.nnn. This statement does not introduce any new code, as its sole task
is to add the namespace aaa.nnn to every class, thereby circumventing errors.

For instance, the Visual Basic.Net compiler would have thrown an error on encountering the
class yyy in the DIM statement, since there is no class called yyy. However, on catching sight of
the 'import' keyword, it adds the namespace to the name of the class, resulting in the name
aaa.nnn.yyy. As there already exists a class by this name, the Visual Basic.Net compiler does
not protest.

In case the error still persists, it takes the next set of Imports statement, and attempts to seek
a match. Only when all the Imports fail to find a match, a "not found class" error is thrown.
Effectively, the imports statement is like a short form, which when introduced, eliminates the
need for mentioning the namespace on every occurrence of the class.

Bear in mind that it is the namespace, and not the name of the class, which is permitted after
the Imports keyword.

a.vb
Class zzz
Shared Sub Main
dim a as yyy
a = new yyy
a.abc
a.pqr
End Sub
End Class
Class yyy
sub abc
System.Console.WriteLine("yyy abc")
end sub
end class
Class xxx
sub pqr
System.Console.WriteLine("xxx pqr")
end sub
end class

Error
c:\il\a.vb(6) : error BC30456: 'pqr' is not a member of 'yyy'.

The above example has three classes, viz. xxx, yyy and zzz. The class xxx has the sub pqr, the
class yyy has the sub abc, while the class zzz has the sub Main.

In Main, an object 'a' is created to be an instance of the class yyy. Thus, the sub abc can now
be called off it. However, calling the sub pqr using the object 'a' results in an error, as the class
yyy does not contain pqr. The class xxx contains the sub of pqr. Thus, a class is a self-
contained unit wherein, the entities belonging to one class cannot be accessed by another
class.

a.vb
Class zzz
Shared Sub Main
dim a as yyy
a = new yyy
a.abc
a.pqr
End Sub
End Class
Class yyy
Inherits xxx
sub abc
System.Console.WriteLine("yyy abc")
end sub
end class
Class xxx
sub pqr
System.Console.WriteLine("xxx pqr")
end sub
end class

Output
yyy abc
xxx pqr

In the above program, we have merely added the word 'Inherits' to the name of a class xxx, on
the line below the class yyy. With this new induction, all the entities present in the class xxx,
now belong to the class yyy too. Thus, all the code and instance variables that are present in
the class xxx, can now be used by the yyy object 'a'.

This is one of the most salient concepts in the world of object oriented programming, wherein,
a class such as yyy can derive itself from a base class like xxx. And by virtue of this, it can
reuse code that is present in this base class. Programmers are very often mentally fine-tuned to
write code in this fashion.

The code is never to be written from a scratch. Instead, it is derived from a class that already
has code present in it. By using 'inherits', the derived class simply inherits everything from the
base class.

Now, insert the following two lines on a single line as in.

Class yyy
Inherits xxx
to
Class yyy Inherits xxx

This action generates the following error:

Error
c:\il\a.vb(9) : error BC30205: End of statement expected.

Even though Visual Basic.Net is known to be an exceedingly flexible language, it still requires
us to adhere to certain rules. Thus, we have no choice but to place the word 'Inherits' on a
separate line. The indentation is for the sake of neatness, and for good luck of course!

a.vb
Class zzz
Shared Sub Main
dim a as yyy
a = new yyy
End Sub
End Class
Class yyy
Inherits xxx
sub new
System.Console.WriteLine("new yyy")
end sub
end class
Class xxx
sub new
System.Console.WriteLine("new xxx")
end sub
end class

Output
new xxx
new yyy

In the above example, the class yyy derives from the base class xxx. Therefore, when an
instance of class yyy is created, two constructors are called, one from the class xxx, and the
other from the class yyy, in the prescribed sequence. This occurs because the class yyy is also
made up of the class xxx. Thus, while an instance of a yyy object is being created, two objects
are created in memory: a xxx object, followed by a yyy object.

During the creation of these two objects in memory, the constructors of these classes get
called. A constructor, as you may recall, is called when an object is created. The constructor of
the base class, i.e. xxx is always called first, followed by the constructor of the derived class
yyy.

a.vb
Class zzz
Shared Sub Main
dim a as yyy
a = new yyy
End Sub
End Class
Class yyy
Inherits xxx
sub new
mybase.New
System.Console.WriteLine("new yyy")
end sub
end class
Class xxx
sub new
System.Console.WriteLine("new xxx")
end sub
end class

Output
new xxx
new yyy
Yet another line has been added to the constructor of the class yyy, i.e. mybase.New. Despite
our not having created any variable or object called mybase, no error is generated. This is
because, every method or function or sub is provided with a free object called mybase, which is
a handle to the base class.

By specifying mybase.New, the constructor of the base class gets called. No specific reason can
be attributed to this, but most code written in the Visual Basic.Net world, calls the constructor
of the base class in the above-mentioned manner. The mybase object is used to access
members of the base class.

a.vb
Class zzz
Shared Sub Main
dim a as yyy
a = new yyy
End Sub
End Class
Class yyy
dim i as integer
sub new
Me.i = 10
System.Console.WriteLine("new yyy {0}" , Me.i)
end sub
end class

Output
new yyy 10

Obtaining a free ride in life has never been too easy. In the Visual Basic.Net world, not only do
we get a free mybase object, but also acquire another object called Me. The Me object is used to
refer to itself, while the mybase object is used to refer to the base class. In the above example,
whether we write Me.i or i, it would mean the same thing.

a.vb
Class zzz
Shared Sub Main
dim a as yyy
a = new yyy(100)
a.abc
End Sub
End Class
Class yyy
dim i as integer = 10
sub new(i as integer)
System.Console.WriteLine("new yyy {0} {1}" , Me.i , i)
Me.i = i
end sub
sub abc
System.Console.WriteLine("new yyy {0} {1}" , Me.i , i)
end sub
end class

Output
new yyy 10 100
new yyy 100 100
The utility of the reserved word Me is evident in the above example, where the class yyy has an
instance variable named i, and the parameter to the constructor is also named i.

In the class zzz, 'a' is created as an object to yyy, by assigning a value of 100 to the
constructor. Doing so would load the yyy object in memory, where the first line is a DIM
statement. The DIM statement utilizes an 'equal to' sign to initialize the variable during its
creation. Thus, the instance variable i is initialized to the value of 10.

Thereafter, the constructor of class yyy is called, where the WriteLine function displays the
value of i. The i being referred to here is the parameter i in the constructor, and not the
instance variable i. This is because the parameters and local variables get higher preference in
a function.

However, using Me.i in the constructor would refer to the instance variable and not to the
parameter. The statement Me.i = i, would initialize the instance variable i to the value stored in
the parameter i. In the sub abc, i and Me.i will always refer to the instance variable, since there
is no parameter named i.

Thus, the keyword Me proves to be of great utility, when there is an instance variable and a
local variable, both having the same name.

a.vb
Class zzz
Shared Sub Main
dim a as yyy
a = new yyy
End Sub
End Class
Class yyy
Inherits xxx
sub new
mybase.New
System.Console.WriteLine("new yyy")
end sub
end class
Class xxx
Inherits ppp
sub new
System.Console.WriteLine("new xxx")
end sub
end class
Class ppp
sub new
System.Console.WriteLine("new ppp")
end sub
end class

Output
new ppp
new xxx
new yyy

In the above program, the class ppp is a self-contained unit, which is not derived from any
other class. Then, the class xxx is derived from class ppp. Hold your horses! There is more to it.
One more class yyy is then derived from the class xxx. As a result, the class yyy is
directly/indirectly derived from the two classes xxx and ppp.

Now, creating an instance of class yyy, would lead to the creation of three objects in memory,
viz. xxx, yyy and ppp. The sequence is important, since the base class constructor always gets
called first.

Thus, the ppp constructor gets called first, the xxx class gets called next, and finally, it is the
yyy class that gets called. The abovementioned rules apply, irrespective of the number of
classes that we derive from.
 

 
3. WinForms
The Visual Basic.Net compiler has a large number of options, one of which is the /target
option, which determines how the output should appear. Under Windows, a dll file and an exe
are both similar. The only apparent distinction between the two is that an exe file can be
executed at the command prompt, whereas, a dll cannot. It acts as a carrier or a reservoir of
code. By default, the target output is set to exe. Now, create the following file called b.vb:

b.vb
public class yyy
sub abc
System.Console.WriteLine("abc")
end sub
end class

The file b.vb is composed into a dll file, instead of an exe file. To achieve this, the following
command is used:

>vbc /target:library b.vb

The above command creates a file called b.dll, which carries the code of the class yyy,
containing a sub called abc. The word 'public' mentioned before the name of the class signifies
that the entire world has access to the code in the class. Words such as 'public' are called
access modifiers, which we shall address in a subsequent section. If we eliminate the 'public'
access modifier, the next program would not work successfully.

a.vb
Public Class zzz
Shared Sub Main()
dim a as yyy
a = new yyy
a.abc
End Sub
End Class

>vbc a.vb /r:b.dll

Output
abc

One of the most vital advantages of writing code is that the other programmers too can access
it. For example, the file b.dll carries a class yyy, which can contain numerous functions like
abc. Now, any other program can also refer to this code.

On running the Visual Basic.Net compiler at this stage, an error will be reported, since the
compiler is unaware of the identity of the file that contains the code. Thus, we need to explicitly
inform the compiler about the identity of the dll file that contains the function.
This is achieved by specifying the command line option /R, where the R stands for 'reference'.
It is followed by the name of the dll that contains the code. Microsoft also offers its own set of
classes by locating them in multiple dll files. Hence, to access the code contained in them, we
need to use the /R option at the compiler level.

This chapter is primarily focused on building GUI applications. In the Visual Basic.Net program
stipulated below, the class zzz is derived from the class Form. This implies that all the code in
the class Form is now made available in the class zzz.

a.vb
Imports System.Windows.Forms
Public Class zzz
Inherits Form
Shared Sub Main()
dim a as zzz
a = new zzz()
Application.Run(a)
End Sub
End Class

>vbc a.vb /r:System.Windows.Forms.dll /r:System.dll

Screen 3.1
On running the above program, general happiness prevails, since a pretty little window comes
into view, as shown in screen 3.1.

Since the class zzz is derived from the Form class, all methods and instance variables within
the Form class, are now made available in zzz. This establishes the potency and beauty of the
concept of inheritance.

The Form class belongs to the namespace of System.Windows.Forms. This explains the
presence of the 'imports' statement, which is provided with the primary aim of evading the use
of the entire namespace before Form, since the class is used very often.

All classes in the .Net world must belong to a namespace. If we do not specify a namespace for
a class, it will get placed in the global namespace.

In the Main function, first an object of type zzz is created. It is then passed as a parameter to
the Run function. Abiding by what we have learnt so far, we can safely conclude that
Application is the name of the class, and Run is a shared function. The Application class also
falls under the namespace of System.Windows.Forms.

The final action to be taken is to furnish the Visual Basic.Net compiler with a reference to the
dll, which contains the code of the Forms and Application classes. The help text on the above-
mentioned classes clearly reveals that the dll that constitutes the System.Windows.Namespace,
is the System.Windows.Forms dll. This name is passed on to the Visual Basic.Net compiler
using the /R option.

The Run function merely requires an object derived from Form. Once it gets the custody of this
object, it displays a cool looking window. Never mind how or why!

a.vb
Class zzz
Shared Sub Main
dim a as yyy
a = new yyy
a.i = 10
a.abc
a.pqr
End Sub
End Class
Class yyy
Inherits xxx
sub abc
System.Console.WriteLine("yyy abc {0}" , i)
i = 30
end sub
end class
Class xxx
public dim i as integer
sub pqr
System.Console.WriteLine("xxx pqr {0}", i)
end sub
end class

Output
yyy abc 10
xxx pqr 30

The above program demonstrates a simple concept. The class xxx has a public instance
variable i. Thus, other classes can now access this variable. The class yyy is derived from the
xxx class, with 'a' as its object. Now, 'a' is allowed access to the variable i.

The golden rule here is that, whatever an object of class xxx is allowed to do with the members
of the class xxx, the same leeway is granted to an object of the class derived from xxx.
Furthermore, there is only one copy of the variable i. So, any changes made to it from the sub
abc, would be reflected in the sub pqr within the class xxx.

The next program converges around this concept.

a.vb
Imports System.Windows.Forms
Public Class zzz
Inherits Form
Shared Sub Main()
dim a as zzz
a = new zzz()
Application.Run(a)
End Sub
sub new
myBase.New
Me.Text = "Vijay Mukhi"
end sub
End Class

Screen 3.2
The above program displays a window, as seen in screen 3.2, where the title of the window is
given as "Vijay Mukhi".

The program has a constructor 'new', which traditionally calls the original constructor. This is
not mandatory, but we would rather follow the rules laid down by the Big Daddy! Then, using
the keyword 'Me', the variable Text is initialised to "Vijay Mukhi". The Text member is not
created anywhere in the class. So, it is safe for us to assume that it exists in the class Form.

This initialisation results in a change in the title of the window. The Run function determines
the title of the window, after ascertaining the contents of the Text variable. The default is a
blank value. We are still at the preliminary stage of customizing the Windows Forms.

a.vb
Imports System.Windows.Forms
Imports System.Drawing
Public Class zzz
Inherits Form
Dim b as Button
Shared Sub Main()
dim a as zzz
a = new zzz()
Application.Run(a)
End Sub
sub new
myBase.New
Me.Text = "Vijay Mukhi"
b = new Button
b.Text = " sonal"
dim p as Point
p = new Point(10,100)
b.Location = p
Dim c as Control.ControlCollection
c = Controls
c.Add(b)
end sub
End Class
>vbc a.vb /r:System.Windows.Forms.dll /r:System.dll /r:System.Drawing.dll

This example displays a button with the name "sonal" in it, as shown in screen 3.3.

Screen 3.3
We initiate the above example by creating an object 'b', whose type is Button, and which
resides in the System.Windows.Forms namespace.

The class contains code and instance variables that are conversant with the role of a
button.When the Text property of the Button object 'b' is set to some text such as "sonal", this
text gets displayed on the Button.

Now, to place the button at a specific position on our screen, the Point class is used, whose two
members represent a Point on the graphics screen. These two members x and y are initialised
to the values 10 and 100, respectively, through the Point constructor. The Point class is in the
System.Drawing namespace. Therefore, during compilation, the reference of /r is set to the file
System.Drawing.dll.

The Location property of the Button object is then initialised to this freshly created Point object
p. The most pertinent action at this stage is to inform the Form class that a Control is being
added to the Form.

The Form class has a property called Controls, whose type is Control.ControlCollection. This
property keeps track of all the controls that are placed on the form.

So, by using the Add function in the Control.ControlCollection, we can add the button control
to the Collection object, thereby displaying the button on the form. This is how we add a
Button Control to our form.

a.vb
Imports System.Windows.Forms
Imports System.Drawing
Public Class zzz
Inherits Form
Dim b,b1 as Button
Dim t as TextBox
Shared Sub Main()
dim a as zzz
a = new zzz()
Application.Run(a)
End Sub
sub new
myBase.New
Me.Text = "Vijay Mukhi"
b = new Button
b.Text = "sonal"
dim p as Point
p = new Point(10,100)
b.Location = p
b1 = new Button
b1.Text = "VMCI"
p = new Point(40,180)
b1.Location = p
Dim s as Size
s = new Size(50,90)
b1.Size = s
t = new TextBox
t.Text = "Hi"
p = new Point(140,100)
t.Location = p
Dim c as Control.ControlCollection
c = Controls
c.Add(b)
c.Add(t)
c.Add(b1)
end sub
End Class

This example culminates in the creation of a window with two buttons, one larger than the
other and a textbox containing the value "hi". This is shown in screen 3.4

Screen 3.4
One more button is added to the form in the same manner as before. Thereafter, the new
instance of the button b1 is provided with a Size object. The Size class comprises of the width
and height members, which are to be initialised using the constructor. The Size property of the
Button class determines the size of the control. In case the dimensions are not mentioned, the
default values are taken, as seen in the case of the Button b.

The TextBox class is used to place a TextBox control on the form. Here also, the Text property
determines the contents that are to be displayed with the TextBox. Everything else remains
just the same.

This example illustrates how multiple controls can be placed on a single form. The only
impediment here is that, each time that the Control is to be added to the form, the services of
the Add function have to be exploited. So, if there are 100 controls to be placed on the form,
the Add function will be executed a 100 times! Let us simplify this process by introducing the
concept of Arrays.
a.vb
Public Class zzz
Shared Sub Main()
dim a(2) as integer
a(0) = 1
a(1) = 10
a(2) = 40
System.Console.WriteLine("{0} {1} {2}", a(0), a(1), a(2))
end sub
End Class

Output
1 10 40

An array is a variable with a single name, but is equipped to hold multiple values. The above
DIM statement of "a(2) as integer" creates three variables named a(0), a(1) and a(2). Thus, in a
single stroke, multiple variables have been created. These variables conduct themselves in a
manner similar to the normal variables. At this stage, the only difference that you need to take
heed of is, that this type of variable has a pair of open-close brackets following it.
a.vb
Public Class zzz
Shared Sub Main()
dim a(2) as integer
dim i as integer
i=0
a(i) = 1
i=2
a(i) = 10
i=1
a(i) = 100
System.Console.WriteLine("{0} {1} {2}", a(0) , a(i) , a(2))
end sub
End Class

Output
1 100 10

The above example showcases the most valuable facet of an array.

First, we create an array of size 3, followed by an integer variable i, which is initialised to a


value of 0. Earlier, we had specified a number in round brackets, but here, we specify the
variable i instead. Thus, the line a(i) = 1 will evaluate to a(0) = 1, since the variable i has a
value of 0. On the next line, we change the value of the variable i to 2. Therefore, the statement
a(i) = 10 evaluates to a(2) = 10. The variable a(1) is initialised in a similar manner.

Hence, in the above example, the value of the variable i determines the name of the variable. To
display the value of the array variables, similar rules apply. The use of variables in lieu of
constant numbers assists in making the code more generic.

Loops

a.vb
Public Class zzz
Shared Sub Main()
dim i as integer
i=1
Do while i <= 5
System.Console.Write("{0} " , i)
loop
end sub
End Class

If you run the above program, it would continue to execute till eternity. So, hold on! Let us
appreciate the code first.

We first create an integer variable i, and set its value to 1. Then, we come across a 'Do while'
statement containing the condition i <= 5. The 'Do while' statement, including the condition
and the loop, are part and parcel of the looping construct. A loop construct is pressed into
action when the same code needs to be repeated multiple times.

The condition i <= 5 will evaluate the value of i. Since it is 1, the condition evaluates to 1<=5,
which is True. Since the condition results in True, all code enclosed within the 'Do while' and
the loop, gets executed. Presently, it contains only a Write function, which is very similar to the
WriteLine function, except for the 'return' at the end. This Write function displays the value of
the variable i.

On encountering the loop statement, the control in code execution loops back to the 'Do while'
statement, where the condition is checked again. Since the value of i is still 1, the condition
results in True, thereby executing the Write Function and the loop.

The loop again takes the control back to the 'Do While' statement, and the whole process
iterates itself. This action will keep repeating itself till eternity, since the condition after the 'Do
While' never becomes False. Thus, we are trapped in an incessant loop. Press Ctrl C to
terminate the loop.

a.vb
Public Class zzz
Shared Sub Main()
dim i as integer
i=1
Do while i <= 5
System.Console.Write("{0} " , i)
i=i+1
loop
System.Console.Write("..{0} " , i)
end sub
End Class

Output
1 2 3 4 5 ..6

In this program, only one additional line has been added to the earlier program. This line
enhances the value of the variable i by 1. This amendment results in printing the numbers
from 1 to 5.

This is because, when the value of the variable i is 1, the condition becomes 1<= 5, which
evaluates to True, and the value of i is printed, using the Write function.

Then, the value is incremented by 1, making it 2. The condition that is now checked is 2<=5.
This too results in True and the process is reiterated. However, when the value of i reaches 6,
the condition evaluates to 6<=5. The outcome of this is False and the loop terminates. The
control is now passed to the next statement after loop. The value of i that is 6, is now printed
with two dots preceding it. A loop construct is implemented when code has to be executed
repetitively.

a.vb
Public Class zzz
Shared Sub Main()
dim i as integer
i=1
while i <= 5
System.Console.Write("{0} " , i)
i=i+1
End while
System.Console.Write(".{0} " , i)
end sub
End Class

Output
1 2 3 4 5 .6

The output of the two programs remains almost identical, except that the double dots are
replaced by a single dot. This is a sure indication of why programming languages exasperate
us! In place of the 'Do While' loop, we now have the 'While End While' loop. This amendment in
the syntax however does not translate into any change in the output.

Thus, it is entirely your decision to use either a 'While End While' or a 'Do While' loop. There
are myriad ways of achieving the same result. However, we would advise you to stick to just
one loop construct under all circumstances.

a.vb
Public Class zzz
Shared Sub Main()
dim i as integer
i = 100
for i = 1 to 5
System.Console.Write("{0} " , i)
next
System.Console.Write(".{0} " , i)
end sub
End Class

Output
1 2 3 4 5 .6

One more variation of the loop construct is the For Next loop. Here, a variable has to be
created, so that it can be used as a counter. We have created an integer variable i and
initialised it to 100. The next line has the 'for' syntax, "for i = 1 to 5".

Despite the variable being initialised to 100, the 'for statement resets its value to 1. Since the
value of i falls within the range of 1 to 5, all the code until the Next statement is executed.

The 'Next' statement is like the loop statement. It moves the control back to the top of the 'for'
loop, but only after incrementing the value of the counter by 1. The code from the 'for' upto the
'next' is then executed once again. This process is repeated until the value of the variable i
reaches 6. As soon as i falls out of range, the line that immediately succeeds the 'for- next'
statement, is executed.

The variable i is not required to be initialised, since the 'for' statement resets its value in any
case.

You are bound to feel perplexed while determining the loop construct to be implemented. You
may adopt our methodology wherein, on Mondays, Wednesdays and Fridays, we use the 'While'
construct, and on Tuesdays, Thursdays and Saturdays, we use the 'For' construct. Sunday
being a rest day, we desist from writing any code. ;-)

a.vb
Public Class zzz
Shared Sub Main()
dim i as integer
dim b(3) as integer
for i = 0 to 3
b(i) = i*10
next
for i = 0 to 3
System.Console.Write(".{0} " , b(i))
next
end sub
End Class

Output
.0 .10 .20 .30

The above example amply demonstrates how arrays work seamlessly with Loop Constructs. We
create an array 'b' of 4 integers and 1 integer variable named i. In the 'for' statement, i will have
values ranging from 0 to 3.

In the first round, when the value of the variable i is 0, the name of the variable becomes b(0)
and its value becomes 0*10 = 0. Then, when the value of the variable i becomes 1, the name of
the variable becomes b(1), and its value becomes 1*10 = 10. In this manner, all the members of
the array are initialised to values that are multiplication factors of 10.

The second 'for' loop is used to display the values of all the members of the array. This is
achieved by using the Write function. Thus, arrays and the loop mechanism go hand-in-glove.
The combination has been employed extensively in this book. Its use has also been witnessed
in many of the other programming languages.

a.vb
Public Class zzz
Shared Sub Main()
dim i as integer
dim b() as integer = {1,20,40 , 100}
for i = 0 to 3
System.Console.Write(".{0} " , b(i))
next
end sub
End Class
Output
.1 .20 .40 .100

Short forms may be considered as a bane or a boon. In the above example, we have
demonstrated how arrays can be created and initialised simultaneously.

Using the = sign with DIM, different array values can be assigned to the members of the array,
by placing the values in a pair of curly brackets {}. The 'for' loop confirms these values in the
array. We could have easily shirked from explaining this concept, but we thought it prudent to
deal with it, since this concept has been encountered on numerous occasions in various Visual
Basic.Net programs.

Failures are considered to be stepping-stones to success. In the same vein, errors in


programming languages are believed to make a programmer a lot wiser. Therefore, we have
sprinkled this book generously with many of these error messages. Also, in a programming
language, every rule that is not abided by, generates a corresponding error message.

For example, if we change the array initialising line to the following:

dim b(3) as integer = {1,20,40 , 100}

the error that is generated is :

Error
c:\il\a.vb(4) : error BC30672: Explicit initialization is not permitted for arrays declared with
explicit bounds.

At the first glance, you may be astounded by the error message, because so far, we have merely
created an array of 4 variables and initialized each of these array members. However,
programming languages in a sense may be compared to the human behavior, especially when it
comes to being extremely moody and whimsical.

In the Visual Basic.Net language, it is very clearly stated that when an array is initialised at the
time of creation, the size must not be specified, even though it may be known in advance. The
compiler is of the opinion that we are encroaching on its prerogative of determining the size of
the array. On various occasions, you will realize that a large number of rules stipulated by the
compiler, have become rigid and redundant. Welcome to the real world !

However, some errors do manage to dodge detection. Thus, even if the program compiles
normally, there is always a possibility of encountering errors on running the program.

Rectify the above error and change the 'for' loop to the following:
for i = 0 to 4

By increasing the range from 3 to 4, one more array member can now be accessed. However,
the array extends only from b(0) to b(3).
While the process of scanning the code is on, the compiler is unable to detect such
programming errors. Therefore, it compiles successfully. However, it is during the execution
when the program tries to access b(4); in other words, when it goes beyond the bounds of the
array, the following exception is thrown:

Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the


array.
Thus, an exception is an error that the compiler fails to detect. It occurs only at run time. This
is an ample proof of the fact that, you can never be absolutely positive about whether your
program will run error-free or not, despite having compiled successfully.

a.vb
Public Class zzz
Shared Sub Main()
dim a as aaa
dim b as bbb
a=b
end sub
End Class
class aaa
end class
class bbb
end class

Error
c:\il\a.vb(5) : error BC30311: Value of type 'bbb' cannot be converted to 'aaa'.

There are great many people in this world, who are very selective about the people that they
associate themselves with. In all probability, they would have picked up this trait from
programming languages like Visual Basic.Net. ;-)

Both, an 'integer' as well as a 'string', are classes that the Visual Basic.Net complier
understands intrinsically. But, when we create two classes aaa and bbb, they are considered
external entities, since Visual Basic.Net is not clued-up about them. Therefore, the classes
have been created explicitly in the program. Now, when we try to equate one with the other, we
get an error.

The fundamental rule in all modern programming languages is that, we cannot equate two
variables or objects with each other, unless they belong to the same class, or are of the same
data type. Therefore, an object of class aaa cannot be equated to an object of class bbb, even
though the contents of both may be the same.

a.vb
Public Class zzz
Shared Sub Main()
dim a as aaa
dim b as bbb
a=b
end sub
End Class
class aaa
end class
class bbb
inherits aaa
end class

However, there are always a few exceptions to every rule. Two different objects can be equated
to each other, provided one derives from the other. Therefore, the only modification that we
have introduced in the above program is that, we have derived the class bbb from the class
aaa. The class bbb now comprises of both aaa and bbb.
The amended rule now reads as follows: Two different classes can be equated, provided the
class on the right of the 'equal to' sign is a derived class, and the one on the left is the base
class. This is permissible since a derived class contains the base class. This rule applies
wherever a data type is expected, such as, the parameters to a function, etc. The same rule
applies to the members of an array also.

a.vb
Public Class zzz
Shared Sub Main()
dim b(2) as aaa
b(0) = new aaa
b(1) = new bbb
b(2) = new ccc
end sub
End Class
class aaa
end class
class bbb
inherits aaa
end class
class ccc
inherits bbb
end class

In this program, three classes are created. The class aaa is an independent entity, which does
not derive from any class. The class bbb derives from the class aaa. Finally, the class ccc
derives from the class bbb.

Thus, the class ccc comprises of classes aaa and bbb, whereas, the class bbb encompasses the
class aaa. Thereafter, an array 'b', of type aaa and size 3, is created.

The member b(0) is initialised to an aaa object. The array member b(1) is set to a bbb type, and
b(2) is equated to a ccc object. None of the above generates an error, since we can equate a
base class to a derived class, bearing the following rule in mind that, the object on the right
should be larger than the one on the left.

a.vb
Imports System.Windows.Forms
Imports System.Drawing
Public Class zzz
Inherits Form
Dim b,b1 as Button
Dim t as TextBox
Shared Sub Main()
dim a as zzz
a = new zzz()
Application.Run(a)
End Sub
sub new
myBase.New
Me.Text = "Vijay Mukhi"
b = new Button
b.Text = "sonal"
dim p as Point
p = new Point(10,100)
b.Location = p
b1 = new Button
b1.Text = "VMCI"
p = new Point(40,180)
b1.Location = p
Dim s as Size
s = new Size(50,90)
b1.Size = s
t = new TextBox
t.Text = "Hi"
p = new Point(140,100)
t.Location = p
Dim c as Control.ControlCollection
c = Controls
Dim a() as Control = {b,b1,t}
c.AddRange(a)
end sub
End Class

We took a small detour into certain aspects of the language to simplify the explanation of the
above example. In order to add controls to the form, the Add function from the Controls
collection, was called multiple times. It is easy to desist from this repetition by using the
AddRange function, which accepts an array of Control objects as a parameter.

So, after creating the individual controls and setting their properties, we then create an array
'a' of type Control. The array is initialised to the three control objects at the time of declaration
itself. The values assigned are the two Button types b and b1 and the TextBox t.

You should be well apprised of the fact that the array type is Control, and the array members
are of type Button and TextBox. The compiler does not gripe at this approach, since the Button
and TextBox classes are derived from the class Control, thereby making Control as the base
class. Now, when we run the program, the controls get displayed on the Form Window.

The next step is to activate some code when the button is clicked upon. To implement this, we
again need to take a break away from the regular routines, and fathom out the language a little
further.

Event Handling

a.vb
Public Class zzz
WithEvents b as yyy
Shared Sub Main()
dim a as zzz
a = new zzz
End Sub
sub new
b = New yyy()
b.pqr
b.abc()
end sub
Sub vijay() Handles b.e
System.Console.WriteLine("Hi")
End Sub
End Class
public class yyy
Event e()
Sub abc()
RaiseEvent e
End Sub
Sub pqr()
System.Console.WriteLine("pqr")
End Sub
end class

Output
pqr
Hi

Let us examine the above program in small parts.

The class yyy has an entity called 'e', which is neither a variable nor an object, but an event.
The 'event' keyword is understood intrinsically by Visual Basic.Net.

The entity 'b' is of type yyy, but since it has the WithEvents keyword associated with it, the
class will be used solely to handle events. We are fully aware that the concept of events has not
been clarified so far.

To initialise the event object 'b', the zzz object has to be created, since it is the constructor or
the sub 'new' of zzz, that actually instantiates it. Then, using b, the two functions pqr and abc
are called off the yyy object. At this point in time, the WithEvents keyword does not prohibit the
yyy type from calling the sub pqr and abc.

The WithEvents keyword aids in working with events, but b will always remain a yyy object.
Thus, this keyword augments the existing features of the object.

In the function abc, there is a statement RaiseEvent within which, the name of the event has
been specified. As always, the round brackets placed after the event name, are optional.

This statement performs a considerable amount of work. It checks for functions in class zzz.
These functions contain the word 'Handles', followed by the object name 'b', and finally,
followed by the event 'e'. It only looks at functions in zzz, because the function abc has been
called from this class.

The function 'vijay' satisfies both these criteria:


• It contains the Handles keyword and the object name 'b'
• It also comprises of the name of the event 'e', which is raised by RaiseEvent.

Just about anything can trigger off an event, such as a mouse-click, or a key press on the
keyboard, or the shutting of a window.

Here, the event is set by calling the function abc. The function calls RaiseEvent, which calls all
functions from the class zzz handling this event object combination. There can be innumerable
such functions, which the event in class yyy is simply unaware of.

a.vb
Public Class zzz
WithEvents b as yyy
Shared Sub Main()
dim a as zzz
a = new zzz
End Sub
sub new
b = New yyy()
b.pqr
b.abc()
end sub
Sub vijay() Handles b.e
System.Console.WriteLine("Hi")
End Sub
Sub mukhi() Handles b.e
System.Console.WriteLine("Hi1")
End Sub
End Class
public class yyy
Event e()
Sub abc()
RaiseEvent e
End Sub
Sub pqr()
System.Console.WriteLine("pqr")
End Sub
end class

Output
pqr
Hi1
Hi

In the above example, the class zzz has two functions, viz. vijay and mukhi, both of which can
handle the event 'e' using the object 'b'. So, when the event 'e' is raised, both the functions vijay
and mukhi get called one after the other. This proves that when the function abc is called,
some code will get triggered off in the class zzz.

a.vb
Public Class zzz
WithEvents b as yyy
WithEvents c as yyy
DIM d as yyy
Shared Sub Main()
dim a as zzz
a = new zzz
End Sub
sub new
b = New yyy()
b.abc()
c = new yyy
System.Console.WriteLine("New Object")
c.abc
d = new yyy
System.Console.WriteLine("New Object1")
d.abc
end sub
Sub vijay() Handles b.e , c.e
System.Console.WriteLine("vijay")
End Sub
Sub mukhi() Handles b.e, c.f, b.f, c.e
System.Console.WriteLine("mukhi")
End Sub
Sub sonal() Handles c.f
System.Console.WriteLine("sonal")
End Sub
End Class
public class yyy
Event e()
Event f
Sub abc()
RaiseEvent e
System.Console.WriteLine("Event")
RaiseEvent f
End Sub
end class

Output
mukhi
vijay
Event
mukhi
New Object
mukhi
vijay
Event
sonal
mukhi
New Object1
Event

The above program is mammoth in size. So, let us address it in small parts. We have three yyy
objects named b, c and d. Since the WithEvents clause has been used only for the b and c
objects, the d object cannot be used for event handling. As was done in the earlier program, we
create a new yyy object and call the abc function.

In the abc function, we first raise the event 'e'. On doing so, the Visual Basic.Net system will
check all the functions that handle the event object b.e. Two functions named vijay and mukhi,
which handle this object event, get called. A point to be noted here is that a sub can handle as
many object event combos as is desired. Then, the Event string is displayed.

Thereafter, another event f is raised. The system now searches all the functions for the
combination of b and f. The only match found is the function mukhi.

Object c is then initialized to new yyy and the Write function displays the string New Object.

Now, function abc is called, using the object c. So, the raise event will check for subs having a
combo object c, and an event e. The subs that correspond to this criterion are Vijay and Mukhi.
And finally, the last RaiseEvent raises the event f, and calls mukhi and sonal.
The object d also calls the function abc, thereby raising both the events e and f. Nothing
transpires because there are no subs that can handle the object d, due to the absence of the
keyword 'WithEvents'. The class yyy is equipped to contain multiple events, such as 'e' and 'f'.
The function abc too can raise multiple events.

a.vb
Public Class zzz
WithEvents b as yyy
Shared Sub Main()
dim a as zzz
a = new zzz
End Sub
sub new
b = New yyy()
b.abc(100)
end sub
Sub vijay(i as integer ) Handles b.e
System.Console.WriteLine("vijay {0}" , i)
End Sub
End Class
public class yyy
Event e(i1 as integer)
Sub abc(j as integer)
RaiseEvent e(j)
End Sub
end class
Output
vijay 100

In the above example, the event e is defined to accept an integer parameter. The brackets are to
be provided for the event entity only, when parameters are to be specified. The name of the
parameter is of no consequence at all.

As an outcome of this modification, when the event 'e' is raised, an integer has to be specified
as the parameter. The event is passed the parameter j, which has been passed from the
constructor to the function abc.

The other amendment that has been initiated is with regard to the sub vijay, which now
requires an integer parameter as part of its signature. The Handles and the WithEvents are
oblivious to the changes that occur when parameters are passed to events.

a.vb
Imports System.Windows.Forms
Imports System.Drawing
Imports System
Public Class zzz
Inherits Form
WithEvents b As Button
Shared Sub Main()
dim a as zzz
a = new zzz()
Application.Run(a)
End Sub
sub new
myBase.New
Me.Text = "Vijay Mukhi"
b = new Button
b.Text = "sonal"
dim p as Point
p = new Point(10,100)
b.Location = p
Dim c as Control.ControlCollection
c = Controls
Dim a() as Control = {b}
c.AddRange(a)
end sub
Sub vijay(s As Object,e As EventArgs) Handles b.Click
MessageBox.Show("Hi")
End Sub
End Class

Let us revert back to the Windows Forms application. Now, when we click on the button,
certain code gets called. The button object b has the WithEvents keyword added to it, due to
which, it could now trap events in the Button object.

Screen 3.5
Yet another change has been initiated wherein, the sub vijay accepts two parameters. Also, the
keyword 'Handles' has been affixed with the name of the Button object 'b' and with the Event
within the Button object Click. Resultantly, when the button is clicked upon, code contained
within the button class gets called.

The framework checks for an event called Click within the class, and it calls the command
RaiseEvent with two parameters. This RaiseEvent searches in the class zzz for a sub that has a
Handles, alongwith an object event match b.Click. Since the match is found in the sub vijay,
the code in vijay gets executed.

This is how we can ensure that our own code gets called, whenever the Click event occurs. The
MessageBox is shown in screen 3.5.

Sub vijay1(s As Object,e As EventArgs) Handles b.Click


MessageBox.Show("Hi1")
End Sub
End Class

For the sake of revision, we add the above three lines of code, just prior to the End Class
statement. The code merely creates a sub vijay1, and employs the 'Handles' keyword to
associate the event Click and object b with the sub. Thus, each time we click on the button,
both subs vijay and vijay1 will be executed. We can associate as many events as we like with a
sub, as has been demonstrated above.

a.vb
Public Class zzz
WithEvents b as yyy
Shared Sub Main()
end sub
Sub vijay() Handles b.e
End Sub
End Class
public class yyy
Event e()
end class
class aaa
inherits yyy
Sub abc()
RaiseEvent e()
End Sub
end class

Error
c:\il\a.vb(14) : error BC30029: Derived classes cannot raise base class events.

Earlier, the class yyy had a sub that raised an event using the statement RaiseEvent, with
event e in the same class. In the above example, the RaiseEvent statement raises an event e
defined in the base class. This is forbidden, as has been indicated by the error message. Thus,
we can only raise events in the very class in which they have been created.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
Sub vijay() Handles b!e
System.Console.WriteLine("Hi")
End Sub
End Class

Error
c:\il\a.vb(5) : error BC30287: '.' expected.

The ! symbol placed between the name of the WithEvents object and the name of the event, is
invalid. The only permissible separator is the dot sign.
 

 
4. Access Modifiers
Wherever we may venture these days, there is an exorbitance of security. Barely a few are
allowed access to the resources that are considered vital. Programming languages like VB also
implement similar curbs. Access is denied to code that is crucial, in order to evade
transgression.

a.vb
Public Class zzz
Shared Sub Main()
dim a as yyy = new yyy
a.i = 10
End Sub
End Class
class yyy
dim i as integer
end class

Error
c:\il\a.vb(4) : error BC30390: 'yyy.i' is not accessible in this context because it is 'Private'.

In this example, we have a class yyy with an instance variable i of type integer. In the sub Main
within zzz, an instance variable 'a' of type yyy is declared and instantiated simultaneously. This
act saves us one line of code.

On the next line, we attempt to initialize the instance variable i to a value of 10. On doing so,
we get an error message informing us about the non-accessibility of the variable, since it is
'private'. This error does not occur when we initialize the variable 'a'.

In VB, every entity that is created, is marked 'private' by default. Thus, in class yyy, the integer
variable i is private, and therefore, completely inaccessible. However, the same rules do not
apply to members of the same class. So, if there had been more members of class yyy, they
could have accessed the integer i with ease.

This privilege is granted exclusively to the members of the class. It is not even extended to the
derived classes or the objects of the class.

On adding the modifier 'public' to the variable, as in "Public dim i as integer", the error
vanishes.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
class xxx
Inherits yyy
sub abc
i = 10
end sub
end class
class yyy
protected dim i as integer
end class

The modifier of 'public' allows access to all and sundry, whereas, the modifier of 'protected'
allows only the derived classes to access the variables.

The class yyy has a 'protected' variable i of type integer. The class xxx derives from the class
yyy, since it has the clause of 'inherits' added to it. Due to this, it inherits all the members
contained in the class yyy. If we now initialize the variable, no errors are generated.

This is because the variable is marked as 'protected', in the absence of which, the same error
as depicted above, would have been displayed. Also, creating an instance of the class xxx, does
not allow access to the protected variables. The keyword DIM is optional, when used in
consonance with access modifiers.

b.vb
public class yyy
friend i as integer
sub abc
i = 100
end sub
end class

We start by compiling the above program b.vb, to create a dll using the following command:

>vbc /target:library b.vb

The file b.dll contains a sub abc, which initializes the instance variable i to 100. Note that the
instance variable is declared as a 'friend'. The program a.vb given below, refers to this 'friend'
variable and amends its value to 10.

a.vb
Public Class zzz
Shared Sub Main()
dim a as yyy = new yyy
a.i = 10
End Sub
End Class

>vbc a.vb /r:b.dll

On compiling, the following error is reported:

Error
C:\il\a.vb(4) : error BC30390: 'yyy.i' is not accessible in this context because it is 'Private'.

The 'friend' access modifier signifies that none other than the entities in the same dll or
assembly can access its variables. Since the instance variable i is being accessed from another
assembly named 'a', an access violation is reported.
The basic rule is being reiterated wherein, the same class is allowed total access to any
member. This behavior is at one extreme of the security spectrum. At the other extreme lies the
'public' modifier where no rules are applicable.

The 'protected' modifier rests in the middle wherein, just as in the case of 'private', the derived
classes are permitted access to the 'protected' members in the class. Members with the 'friend'
modifier, are accessible from the same assembly that contains their declaration.

a.vb
Public Class zzz
Shared Sub Main()
dim a as yyy = new yyy
a.abc(100)
a.abc("hi")
End Sub
End Class
class yyy
Overloads sub abc( i as integer)
System.Console.Writeline("yyy abc {0} " , i)
end sub
Overloads sub abc( s as string)
System.Console.Writeline("yyy abc {0} " , s)
end sub
end class

Output
yyy abc 100
yyy abc hi

This program certainly is no path-breaker. The class yyy has two subs with the same name of
'abc', but with different parameter types. Moreover, a new keyword of 'Overloads' has been
incorporated within the function.

The main function in the class zzz first creates an instance variable 'a' of type yyy. Thereafter, it
calls the function abc twice, but each time, it uses distinct parameters.

The keyword 'Overloads' apprizes VB of the fact that the class contains more than one sub with
the same name. This keyword is optional. So, when it is removed, everything would continue to
work as before. However, if we obliterate one of the 'overloads' from any of the abc functions,
the following error would be generated:

Error
c:\il\a.vb(12) : error BC31409: sub 'abc' must be declared 'Overloads' because another 'abc' is
declared 'Overloads'.

The rule is very straightforward: Either use the keyword 'Overloads' in all subs having the same
name, or simply steer clear of it!

a.vb
Public Class zzz
Shared Sub Main()
dim a as xxx = new yyy
dim b as yyy = new yyy
a.abc
b.abc
End Sub
End Class
class yyy
Inherits xxx
sub abc
System.Console.Writeline("yyy abc")
end sub
end class
class xxx
sub abc
System.Console.Writeline("xxx abc")
end sub
end class

Warning
c:\il\a.vb(11) : warning BC40004: sub 'abc' conflicts with sub 'abc' in the base class 'xxx' and so
should be declared 'Shadows'.

Output
xxx abc
yyy abc

The above example does not generate any errors, however it displays a warning. A warning is a
very benign complaint. Hence, it allows the compiler to create an executable file. However, it is
in our own interest that we pay heed to these warnings, because if we snub them now, there is
a strong probability of facing embarrassment at a later stage.

The class xxx is the base class with one sub abc. The class yyy derives from the class xxx, and
it also has one sub called abc. The warning is generated because the subs have the same name
in both, the base class and the derived class. The compiler expects the sub in the derived class
to be assigned a distinct or a new name.

On adding the keyword 'Shadows' to the sub abc, as in 'Shadows sub abc' in the class yyy, the
warning disappears.

In one of the earlier chapters, we had learnt that a derived class can replace a base class.
Accordingly, we create an object 'a' of type xxx, and initialize it to a derived class object xxx.
Then, the sub abc is called off it.

The output shows that the sub abc from class xxx gets called, even though the object has been
initialized to a yyy object. This is because the data type of the object calling the sub, is
accorded preference over the others. Also, due to the 'shadows' keyword, the compiler refers to
the subroutine abc in the yyy, in a very different manner.

The second object 'b' of type yyy calls the sub abc from yyy, since there is no name-clashing in
this respect, and a sub called abc exists in class yyy. If the compile time data type specified in
the DIM statement and the run time data type stipulated in the 'new' are identical, it does not
create any problems.

The object is created as per the run time data type specified, when the 'new' statement is
executed. However, we have the option of calling the subs either from the base type or from the
derived type. In this example, the abc subroutine is called from the base type.
In the next round, we would like to call the sub off the run time data type, i.e. yyy, rather than
off the compile time data type, which is the default.

a.vb
Public Class zzz
Shared Sub Main()
dim a as xxx = new yyy
dim b as yyy = new yyy
a.abc
b.abc
End Sub
End Class
class yyy
Inherits xxx
Overrides sub abc
System.Console.Writeline("yyy abc")
end sub
end class
class xxx
Overridable sub abc
System.Console.WriteLine("xxx abc")
end sub
end class

Output
yyy abc
yyy abc

The above output confirms that the sub abc called off the object 'a', is from the derived class
yyy. Thus, the code has been called from the derived class and not from the base class.

This has been achieved by adding two keywords of 'Overrideable' and "Overrides'.

We first aspire that the abc in the base class xxx should allow the derived classes to override it.
To implement this, the sub abc in class xxx must contain the keyword 'Overridable'. This
keyword informs the compiler that the sub abc allows derived classes to override it.

This is not all! The derived class must also state expressly that it wants to override the abc sub
in the base class. For this very reason, the Overrides keyword has been employed. Thus, the
object 'a' now calls the sub abc from yyy, which is the run time type.

The presence of both the keywords 'Overridable' and 'Overrides', is absolutely imperative. If the
'Overrides' keyword is omitted in the derived class, the default keyword of 'shadows' will be
pressed into action. As a consequence, the compile time data type would then take precedence.
Thus, the sub abc will be called from the class xxx. However, if we specify the 'Overrides'
keyword in the derived class and omit the 'Overridable' keyword from the base class, it will
result in the following error:

Error
c:\il\a.vb(11) : error BC31086: 'abc' overrides a sub in the base class 'xxx' that is not declared
'Overridable'.
This error signifies that the base class must authorize the derived classes to override its
members. The derived class would then be free to decide whether it wants to override it or not.

On certain occasions, there exists specific code that must not be overridden under any
circumstances. Under such situations, the base class can simply ignore the 'Overridable'
keyword, thus eschewing such a eventuality altogether.

One more combination that is not permissible with the sub abc in class yyy is 'Overrides
Shadows sub abc'.

The error message generated is self-explanatory:

Error
c:\il\a.vb(11) : error BC31408: 'Overrides' and 'Shadows' cannot be combined.

The keyword 'Shadows' conceals the derived class sub name from the base class, whereas, the
'overrides' does just the reverse! The 'Overrides' keyword ensures that it is the sub from the
derived class that gets called, and not the one from the base class. Thus, these two keywords
are totally incompatible with each other.

a.vb
Public Class zzz
Shared Sub Main()
dim a as xxx = new yyy
a.abc
End Sub
End Class
class yyy
Inherits xxx
OverLoads Overrides sub abc
System.Console.Writeline("yyy abc")
MyBase.abc
end sub
end class
class xxx
overridable sub abc
System.Console.Writeline("xxx abc")
end sub
end class

Output
yyy abc
xxx abc

We have a single object 'a' of type xxx, which is initialized to a yyy type. Now, when the abc
subroutine is called, it gets called from the class yyy, and not from the base class.

Nevertheless, at times, we would want to explicitly call the base class, instead of the derived
class. To do so, the 'MyBase' keyword is used in the derived class containing the sub. The
'OverLoads' keyword is optional. So, even when there is no recurrence of the abc subroutine, it
brews no trouble.

a.vb
Public Class zzz
Shared Sub Main()
dim a as xxx = new yyy
'a.abc
End Sub
End Class
class yyy
Inherits xxx
OverLoads Overrides sub abc
System.Console.Writeline("yyy abc")
MyBase.abc
end sub
end class
class xxx
protected overridable sub abc
System.Console.Writeline("xxx abc")
end sub
end class

Output
c:\il\a.vb(9) : error BC30266: 'Public Overrides Overloads Sub abc()' cannot override 'Protected
Overridable Sub abc()' because they have different access levels.

One minor rule related to overriding is that, the access modifiers to the functions must remain
the same. In the above example, the sub abc contains the access modifier of 'protected', which
is not present in the derived class, and thereby yields the error.

Thus, the access modifiers to the function or to the sub, must be the same or else, an error
gets generated.

a.vb
Public Class zzz
Shared Sub Main()
dim a as zzz
if a is nothing then
System.Console.WriteLine("First")
end if
a = new zzz
if a is nothing then
System.Console.WriteLine("Second")
end if
End Sub
End Class

Output
First

In this chapter, the above program commenced by declaring a zzz object named 'a'. The object
is yet to be initialized, and upto this point, it is devoid of any value.

This can be substantiated by using the 'is nothing' clause in an 'if' statement. The 'is' operator
ascertains whether the object on the left has the value of the entity specified on the right or
not.
The word 'nothing' is a keyword, which represents an uninitialized value. Accordingly, the first
'if' statement returns a true, while the second 'if' statement returns a false, as the object 'a' has
now been initialized.

a.vb
Public Class zzz
Shared Sub Main()
dim a as integer
if a is nothing then
System.Console.WriteLine("First")
end if
End Sub
End Class

Error
c:\il\a.vb(4) : error BC30020: 'Is' requires operands that have reference types, but this operand
has the value type 'Integer'.

We start by declaring 'a' as an integer, and then, deploy the same 'is' operator on it. An error
gets reported because the 'is' operator works only on reference types, and not on value types.

There are two basic types in VB:


• The value types or the inbuilt types that we encountered earlier, where only the DIM
keyword is sufficient to create variables, without the need of any instance or of 'new'.
• The reference types that include the user-defined ones, and the other types that do not
fall within the purview of value types.

a.vb
<aaa>Public Class zzz
Shared Sub Main()
End Sub
End Class
class aaa
Inherits System.Attribute
end class

Any entity placed within angle brackets, is called an 'attribute'. Thus, aaa is an attribute,
which is placed over the class zzz. An attribute signifies a class that has been derived from the
class Attribute, in the System namespace. An attribute has scores of applications, which we
shall delve upon later.

a.vb
' Honey I am home
Public Class zzz
Shared Sub Main()
End Sub
End Class

No errors are generated in spite of the 'inserted' statement being present on the first line. This
is because a single inverted comma indicates a comment line, which is completely ignored by
the compiler. Programmers insert comments in order to explicate code.
Also from the comment sign upto the end of the line, the statement gets ignored by the
compiler.
Every programmer is generally under the delusion that someday, someone would read his/her
code and judge him/her to be the smartest programmer to walk the terra-firma! The
explanations placed within comments serve as a lode star, when the existing program has to be
enhanced or modified, however, this appears to be a real daunting task!

We commenced this book with a simple Visual Basic project, where the last task that we
undertook, was to display a button on our screen. Then, we took a diversion to explore the
language.

This was absolutely imperative since Visual Studio.Net generated a million lines of code during
the creation the form window and the button. It would have been an uphill task to try and
decipher the code in the first chapter itself, since we were very naive about the language and its
working at that time.

Now that we are comfortable and at home with the concepts of the language, let us endeavour
to discern this code, and also to embellish it with some of our code. Start the Visual Studio.Net
program. The screen that will appear, is evident in screen 4.1.

Screen 4.1
Our Start page lists only one project, i.e. the one that we recently toiled on. Your screen could
have a list of many such projects.

Click on the project called vvv to arrive at the screen painter, as shown in screen 4.2. Now,
double click on the button.
Screen 4.2
Doing so, would open up a new window called the code painter and the cursor would get
positioned at the following function:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button1.Click
MessageBox.Show("Hi")
End Sub

We shall be explaining this function shortly, but prior to that, we want you to append the line
MessageBox.Show with "Hi", as the string parameter to it. Now, press the F5 key, which is a
short cut for compiling and running the program. Along the way, we shall introduce you to a
number of these short cuts, which shall step up your pace of working with Visual Studio.

On clicking the button, a message box appears, which is similar to what we had encountered
in the Events section. Now, close the application and revert back to Visual Studio. The code
that is displayed in the window, is shown in the screen 4.3.
Screen 4.3
Select the entire code and paste it into an editor, such as Notepad.

You may also click on the 'plus' sign and view the actual code generated by the framework. We
have reproduced the code below, after stripping away the superfluous portions.

Form1.vb
Public Class Form1
Inherits System.Windows.Forms.Form
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
End Sub
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
Friend WithEvents Button1 As System.Windows.Forms.Button
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.Button1 = New System.Windows.Forms.Button()
Me.SuspendLayout()
'
'Button1
'
Me.Button1.Location = New System.Drawing.Point(88, 96)
Me.Button1.Name = "Button1"
Me.Button1.TabIndex = 0
Me.Button1.Text = "Button1"
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(292, 273)
Me.Controls.AddRange(New System.Windows.Forms.Control() {Me.Button1})
Me.Name = "Form1"
Me.Text = "Form1"
Me.ResumeLayout(False)
End Sub
#End Region
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles Button1.Click
MessageBox.Show("Hi")
End Sub
End Class

The code produced here is without the redundant blank lines. We believe that unless we have
deciphered the code generated by the framework, we would never be at ease with the product.
Besides, without a thorough understanding, it becomes exceedingly arduous to augment the
existing code.

The code begins with a class called Form1, which is derived from the class Form. Since the
'imports' statement is absent, the Form class is prefaced with the namespace. Any line
beginning with the # character is called a 'directive'. Thus, #Region is a directive, which as
usual, ends with End Region. Clicking on the plus sign with # Region for Windows Form
Designer in Form1, would result into a display of the code generated by the framework.

Thus, whenever the Region directive is encountered, all the code following it upto the 'End
Region' directive, is concealed. Furthermore, any string placed after the Region directive within
the code, is displayed as help. This feature facilitates segregation of code of certain types. Thus,
by placing the Region directive in a class, it becomes much more convenient to expand and
contract the code of a class. Also, it enables the code painter to display a larger number of
program lines, since there is not much space available on the screen. In this case, the Region
directive encapsulates all the code generated by Visual Studio.Net.

There is no Sub named Main visible anywhere in this generated code. However, its existence is
taken for granted. The constructor or the sub 'new', is the first one to be executed. The code
embodied in it first calls the original constructor from the Form class, which is optional. Then,
it proceeds to call the function InitializeComponent. Let us now press on with this function.

The Sub InitializeComponent is private, and it is tagged with an attribute of


DebuggerStepThrough from the namespace System.Diagnostics. At this stage, this attribute
does not assume much significance, and even if we delete it, heaven will not fall upon our
heads!

Thereafter, a new instance of a Button object called Button1 is created. Button1 now becomes
an instance variable, defined with the 'friend' access modifier and the 'WithEvents' keyword,
which allows the object to handle events. The software developer who wrote this program to
generate the VB code, was over-cautious and thus, tagged everything with the word 'Me';
however, this can be safely ignored.

A large number of controls can be added to the form, but with the addition of every control, the
form has to be redrawn. This makes the User Interface extremely clumsy and unwieldy. So, in
order to suspend the process of laying out the Controls on the form, the Form designer is
requested to suspend drawing, till all controls have been rested in place. They can all be
designed in one single stroke.

The function SuspendLayout ensures that the Layout process is suspended for the moment.
Once the code for all controls has been entered, the function ResumeLayout is executed, thus
signaling the Form to display all the controls.

The Code Writer writes the code in a very systematic manner. First, the mandatory button
properties such as Location, Name, Text and TabIndex are initialized to specific values. The
TabIndex property is used to determine the control that should gain focus, whenever the tab
key is pressed. For the moment, the 'name' property is not used.

Then, the Form properties such as ClientSize and AutoScaleBaseSize are initialized. The
ClientSize property determines the initial window size, whereas, the AutoScaleBaseSize
member decides the minimum size to which the form window can be minimized.

The AddRange function is passed an array of Controls, which currently has only one member
of the Button object, since the form has only a single control placed on it. The title of the Form
is also set to a value, and like the Button, it is also assigned a name.

Visual Studio.Net is inherently aware that the Click event is the default event for the button.
Therefore, on double clicking on the button, it writes a sub named Button1_Click. The name
consists of the name of button object, followed by the word 'Click'.

This sub is passed two parameters with the 'Handles' keyword, having Button1 and Click, and
thereby handling the click event of the button. Thus, each time the button is clicked, the
Button1_Click sub gets called.

The last function that screams for attention is named Dispose. The language keyword 'new'
creates an object. However, there is no corresponding 'delete' keyword for destroying the object.

As per the latest trends in programming languages, an object can be created explicitly, but it is
the prerogative of the system to decide when the object should die. In programming languages,
this concept is given the nomenclature of 'garbage collection'. By convention, the Dispose
function is called whenever the objects need to clean things up.

There can be multiple dispose functions in a program. The word OverLoads is optional, which
you may recall, implies that the Form class has a similar function containing the keyword
'overridable'. The 'protected' modifier is applied to the function, because the original sub also
contains it.

Then, we check whether the instance variable in the form has been instantiated or not. If the
parameter contains some value, it is assumed that the object has been created. So, the Dispose
function is called off this IContainer object. The line demonstrates good programming style,
which demands that the similar function of the base class be called.

Barring the 'not' keyword that converts a True to False and vice-versa, we have expounded
every single concept in the above code, by means of small program snippets. Our approach in
the remaining chapters would be to build VB applications, and simultaneously, attempt at
comprehending the VB code that gets generated.

Our programs will be pint-sized, since only then is it feasible to explicate the concept lucidly.
We will go to the extent of explaining every keystroke that is pressed. So, if you follow our
instructions meticulously to the 'T' and not go astray, you would be able to build complex
windows applications with considerable ease.

It would be a revelation to you that when we began learning a product like Visual Studio.Net, we
found it slightly complicated. This was because the diminutive details of the product had to be
unraveled, while trying to learn the approach that Visual Studio.Net expected us to adopt.
Therefore, we have tried to make this voyage as comfortable as possible for you.

 
5. Database Applications
This chapter focuses on building applications that interact with data stored in databases.
However, prior to plunging right in, we need to understand the new concepts of Properties and
Indexers, which we would be bumping into with a surprisingly high frequency, in this
application.

a.vb
public class zzz
shared sub Main()
dim a as yyy
a = new yyy
System.Console.WriteLine(a.abc)
end sub
end class
public class yyy
function abc as integer
System.Console.WriteLine("Before abc")
abc = 200
return 120
System.Console.WriteLine("After abc")
end function
end class

Output
Before abc
120

In the above example, we revisit the concept of a function, which is a sub with a return value.
A function can return values by assigning a value to the function name. There is nothing
incongruous about this procedure of sending back values. However, there is an alternate
approach that is pursued by most of the programming languages, i.e. the use of the 'return'
statement.

In the above program, the function name is set to a value of 200, which becomes the return
value. However, on the next line, we come across a return statement containing a value of 120.
On encountering the 'return' keyword, all execution of code in the function ceases, and the
value of 120 is returned. Thus, the WriteLine function in main, displays the value of 120.

Note that the last WriteLine function in abc, does not get executed. A return statement is used
to return values. The code that is positioned after the 'return' statement, never gets executed.

a.vb
public class zzz
shared sub Main()
dim a as yyy
a = new yyy
System.Console.WriteLine(a.aa)
a.aa = 100
System.Console.WriteLine(a.aa)
end sub
end class
public class yyy
Public Property aa() As Integer
Get
System.Console.WriteLine("get")
Return 10
End Get
Set (i As Integer)
System.Console.WriteLine("set {0}",i)
End Set
End Property
end class

Output
get
10
set 100
get
10

Although the instance variables are very handy, they have two major flaws:
• Firstly, there is no way of executing any code of our own, when the variable is being
accessed. Owing to this limitation, we are never notified about the changes that the
variable undergoes. Thus, no action can be taken when the value changes.
• Secondly, there is no means of performing any error checks on the value. Thus, if the
value goes beyond a certain range, no corrective action can be initiated.

The solution to both these foibles can be seen in the above example, where a yyy object is
created; thereafter, the value of the aa member is displayed. Superficially, the syntax that is
used, appears to be analogous to the one used with instance variables.

However, the code reveals that aa is a property that returns an integer. Its syntax has two
accessors, a Get and a Set accessor. The code between the Get and End Get gets called
whenever the value of the property has to be retrieved; and the code within Set and End Set
gets executed when a value is assigned to the variable.

Each of these accessors could contain a million lines of code. Furthermore, the Get accessor
uses the 'return' statement, where the value to be returned is the current value in the property.
This explains why the value of 10 is displayed by the WriteLine function.

On the face of it, a property behaves just like an instance variable. It is initialized in a fashion
similar to that of a normal variable, except for the fact that the Set accessor code gets executed
when the value is assigned. Thus, the variable i will now hold a value of 100. We have not
executed any other task here. Therefore, the final value displayed is 100 and not 10. However,
in real life, this value must be stored for further processing. The next program fixes this minor
fault.

a.vb
public class zzz
shared sub Main()
dim a as yyy
a = new yyy
System.Console.WriteLine(a.aa)
a.aa = 100
System.Console.WriteLine(a.aa)
end sub
end class
public class yyy
dim ii as integer = 200
Public Property aa() As Integer
Get
System.Console.WriteLine("get")
Return ii
End Get
Set (i As Integer)
System.Console.WriteLine("set {0}",i)
ii = i
End Set
End Property
end class

Output
get
200
set 100
get
100

In this program, 'a' is an object of class yyy. After creating the object, the value contained in
the aa variable is first displayed. Then, the aa member in the object is initialized to 100.
Finally, the value of aa is displayed. At this stage, it is well nigh impossible to distinguish
whether aa is an instance variable or a property.

However, the class yyy clears away the mist of perplexity. When the yyy object is created, an
instance variable ii is set to the value of 200. Now, to display the value of the property aa, the
Get accessor is called, which returns the value of ii, i.e. 200. Thus, even when the property is
not initialized, its value is displayed as 200.

When aa is assigned a value of 100, the Set accessor of aa is called. The value of 100 is passed
as a parameter to Set. This is verified by displaying the value of the parameter i. Subsequent to
this, the instance variable ii is assigned this value. In the next round, when the value of the
property aa has to be retrieved, the value of ii in the Get accessor shall be 100. Thus, the Get
accessor returns the updated value of the variable ii, which is set in the Set accessor code.

In order to store the value assigned to the property, the presence of an instance variable in this
class is imperative. In the same vein, any amount of code can be supplied in the accessors.
This code will be executed when the value of the property is either set or is retrieved.

a.vb
public class zzz
shared sub Main()
dim a as yyy
a = new yyy
System.Console.WriteLine(a.aa)
a.aa = 100
System.Console.WriteLine(a.aa)
end sub
end class
public class yyy
dim ii as integer = 200
Public Property aa() As Integer
Get
System.Console.WriteLine("get")
Return ii
End Get
Set (i As Integer)
System.Console.WriteLine("set {0}",i)
if i <= 50 then
ii = i
else
ii = 50
end if
End Set
End Property
end class

Output
get
200
set 100
get
50

In the above example, we perform a check on the value assigned to the property. In the Set
accessor, if the value of the property aa is ever set to 50 or more, the value of the instance
variable is confined to 50. This occurs only when the Set accessor is called, i.e. at the time of
assignment. The Get would firstly display the initial value of variable ii, which is set to 200.

Now, when a value of 100 is assigned to the property, the 'if' statement in the Set returns
False, thereby executing code in the 'else' clause. As a result, the value of ii is set to 50. Thus,
the error check in the Set accessor introduces some sanity, wherein the user is allowed to
change a particular value.

The best example of this is the GraphType. Altering its value would lead to the execution of a
considerable amount of code, which eventually redraws the graph with the new type that is
specified. At this point, the values need to be validated, since they cannot go beyond their
bounds.

Thus, the beauty of properties is that, they provide us with instant feedback.

a.vb
public class zzz
shared sub Main()
dim a as yyy
a = new yyy
System.Console.WriteLine(a.aa)
end sub
end class
public class yyy
dim ii as integer = 200
Public Property aa() As Integer
Get
System.Console.WriteLine("get")
Return ii
End Get
End Property
end class

Error
c:\il\a.vb(10) : error BC30124: Property without a 'ReadOnly' or 'WriteOnly' specifier must
provide both a 'Get' and a 'Set'.

Due to the absence of the Set accessor in the example just discussed, the above error is
generated. We have not expounded this error message on purpose, since it is self-explanatory.

a.vb
public class zzz
shared sub Main()
dim a as yyy
a = new yyy
System.Console.WriteLine(a.aa)
end sub
end class
public class yyy
dim ii as integer = 200
Public ReadOnly Property aa() As Integer
Get
System.Console.WriteLine("get")
Return ii
End Get
End Property
end class

Output
get
200

There could be many situations wherein we would want to display a property to the user, but
would not want the user to change its values. An answer to this would be to define the property
as ReadOnly.

In this case, the above property shall always have a value of 200. Therefore, we can get rid of
the instance variable ii. All the other advantages of properties remain untouched.

a.vb
public class zzz
shared sub Main()
dim a as yyy
a = new yyy
a.aa = 100
' System.Console.WriteLine(a.aa)
end sub
end class
public class yyy
Public WriteOnly Property aa() As Integer
Set ( i as integer )
System.Console.WriteLine("set {0}" , i)
End Set
End Property
end class

Output
Set 100

Similar to the ReadOnly property is the WriteOnly property. In WriteOnly property, the only
option available to the user is, to change the value of the property.
The above program reveals the fact that, we are permitted to set the value of the property to
100. There are a very few such situations, wherein you may like to use a WriteOnly property.

Now, remove the comment from the above program. On doing so, the screen fills up with errors
akin to the one displayed below:

'Public Shared Overloads Sub WriteLine(format As String, ParamArray arg() As Object)': Property
'aa' is 'WriteOnly'.

This ensues because of the property aa being WriteOnly. Thus, under no circumstances are we
allowed to display its value.

a.vb
public class zzz
shared sub Main()
dim a as yyy
a = new yyy
a.aa("Hi") = 100
System.Console.WriteLine(a.aa("Bye"))
end sub
end class
public class yyy
Public Property aa(i As string) As Integer
Get
System.Console.WriteLine("Get {0} " , i )
Return 25
End Get
Set (ByVal v As Integer)
System.Console.WriteLine("Set {0} {1}" , i , v)
End Set
End Property
end class

Output
Set Hi 100
Get Bye
25

Another advantage of using properties is that, they can accept parameters. The above example
has the property 'aa', which is called with a string parameter.

The property definition is now amended to accept parameters, in a manner similar to a


function. Thus, while setting the value for the property aa, the parameter value of "Hi" is
assigned to it. The value in the parameter is accepted in the string variable i, which is
displayed using the WriteLine function. In a similar manner, the Get accessor is called with the
value of "Bye".
This method is exploited very often, whenever the class represents a record from a table. The
parameter is a string that denotes the field name whose value we may wish to retrieve or set.

a.vb
public class zzz
shared sub Main()
dim a as yyy
a = new yyy
a(10) = 100
a.Item(20) = 200
System.Console.WriteLine(a(30))
end sub
end class
public class yyy
Default Public Property Item(i As Integer) As Integer
Get
System.Console.WriteLine("Get {0} " , i )
Return 25
End Get
Set (ByVal v As Integer)
System.Console.WriteLine("Set {0} {1}" , i , v)
End Set
End Property
end class

Output
Set 10 100
Set 20 200
Get 30
25

The above program has a property called Item, which accepts one parameter of type integer.
Moreover, it has a new keyword called 'default' added to it. When a property is marked as
'default', it requires a parameter; but more importantly, it signifies that this property name can
be omitted.

The property gets called when the yyy instance 'a' is used with the parameter, however, without
a property name. When we come across the line a(10) = 100, Visual Basic.Net hunts for a
property that has the 'default' keyword attached to it. Since the Item property has this tag
added to it, the Set accessor of this property gets called. In the same way, the Get accessor gets
called when the WriteLine function is executed.

However, nothing prevents us from using the property name as in a.Item(20). This is
permissible, even though the property name is 'defaulted'. The concept of 'default' is also
known as an Indexer in languages, such as C#. It is one of the various novel features of the
modern programming language theory.

If there are two properties with the same modifier of 'Default', the following error is reported :

c:\il\a.vb(20) : error BC30359: 'Default' can be applied to only one property name in a class.

This appears logical, since only a single property can be the default value. If there happen to be
two default values, it results in a dilemma as to which property is to be used. It is also
mandatory to assign a parameter to the property. If this is not done, the following error will be
reported :

Default Public Property Item() As Integer

c:\il\a.vb(11) : error BC31048: Properties with no required parameters cannot be declared


'Default'.

After having elucidated these concepts on properties and indexers, we progress on to the next
program, which displays data in a grid. This data is retrieved from a database, and stored in a
database.

a.vb
imports System
imports System.Data
imports System.Drawing
imports System.Windows.Forms
imports System.Data.SqlClient
imports System.Xml
public class zzz
Inherits Form
Dim d as DataGrid
dim c as DataSet
DIM con as SqlConnection
dim cust as SqlDataAdapter
sub new()
d = new DataGrid()
d.Size = new Size(584, 336)
ClientSize = new Size(600, 413)
con = new SqlConnection("uid=sa;database=northwind")
cust = new SqlDataAdapter ("Select * from Customers", con)
c = new DataSet()
d.DataSource = c
Cust.Fill(c, "ccc")
d.DataMember = "ccc"
Controls.Add(d)
end sub
shared sub Main()
dim a as zzz
a = new zzz
Application.Run(a)
end sub
end class

When we run the above program, we see a window with a data grid, displaying data in the rows
and columns format.

Let us unravel the mystery behind how the above program works its magic.
The program begins with six import statements, since it employs the classes that belong to
them. It is extremely cumbersome to recall the namespace that the class belongs to. Therefore,
the help on the classes can be consulted, which not only provides the name of the namespace,
but also enlightens us about the name of the assembly or dll file in which the code resides.
There are far too many classes that Visual Basic.Net employs. So, it is futile to even attempt
committing any of them to memory.
We have adopted the following approach: We incorporate a class in the program. If the compiler
reports an error, we look up the help on the class, which not only divulges the name of the
namespace, but also makes known the assembly encompassing its code. The namespace is
added to the code using the import statement, and the assembly is provided to the compiler
with the /r: option.

After determining all the namespaces and the dll files that are required, the final compiler
command looks like the followings:

vbc a.vb /r:System.Windows.Forms.dll /r:System.dll


/r:System.Drawing.dll /r:system.data.dll /r:System.XML.dll

Screen 5.1
Four objects named d, c, con and cust are declared. Each of them is assigned a special role to
play. We shall systematically explain their significance.

The class zzz inherits from the Form object, thereby including all the members of the Form
class. In the Sub main, 'a' is created as an object of type zzz, which is then provided as a
parameter to the Run function in the Application class. While creating the object, the
constructor or the 'new' function of zzz, gets called. In this sub, a DataGrid object is
instantiated. The DataGrid class contains code that performs certain tasks, such as, displaying
data in rows and columns format.

The Size property is set to display the data grid within a specified area. Most properties have a
default size. However, in the case of the DataGrid, the default size is too diminutive for our
liking. The ClientSize property determines the size for the entire window, which is also set to a
new value, since the default size is too small.

The SqlConnection object is the next in line to be created. It is this object that connects to an
SQL Server database. While connecting to the SQL Server, this class requires certain data,
such as the userid, the password, the database, etc. This is because a database is susceptible
to access by trillions of people. Thus, to maintain integrity of data, only authorized users
should be allowed access to it. For the neophytes of the database world, Oracle and SQL Server
are the two largest database products in the world. A database server is a program that can
store stupendous quantities of data.

The string passed to the SqlConnection constructor is called a 'connection string', which uses
the semicolon symbol as a separator or delimiter. The word uid is an option given with the
connection and it represents the user name.
Every database or operating system, or any multi-user entity, has a specific user, for whom no
rules are applicable. In Oracle, this user is known as 'System', while in SQL Server, it is known
as 'sa'. Thus, the user 'sa' is logging into the database. It does not possess any password,
because while installing the server, we selected the blank password option.
We are aware that this is indeed a very dumb act to perform and it is unlikely to transpire in
real life. However, while writing this book, we wanted to avoid all complications, and hence, we
left it blank.

The data that is stored in the tables, requires to be logically grouped, similar to the manner in
which classes are grouped within namespaces. This concept is termed as 'databases', implying
that the data would be stored in tables within a particular database in SQLServer. The string
has the database connection option, which is set to the database named Northwind, from
which, tables would be extracted for use.

The lone object that we have achieved so far is that, we have framed the connection string in
the SqlConnection class, and have stored the result in the SqlConnection object 'con'. We have
not made any attempt to connect to the database as yet.

To execute this activity, an instance of SqlDataAdapter is created and stored in the variable
called 'cust'. The constructor is assigned two parameters; the first parameter is a string and
the second one is the freshly created SqlConnection object called 'con'.

The string in the first parameter informs the SqlDataAdapter class about the data that needs to
be retrieved from the database. Hence, it has to be specially constructed in accordance with the
rules of SQL, i.e. Structured Query Language. Tons of pages have been written about SQL.
However, it shall be our endeavour to explain SQL in less than 50 words.

The word Select is used in SQL languages to retrieve data from a database. Following this, the
field or column names that are to be retrieved, are specified. The * sign represents all the
columns in the table. The word 'from' is part of the syntax. It has to be followed by the name of
the table of the database. Here, the table Customers is specified. These databases have already
been created and filled with data during the installation of the server.

Thereafter, the SqlDataAdapter object collects the following:


• The SQL statement, which is used for retrieving data.
• The connection object, which is used for connecting to SQL server.

Once the connection string is prepared and the data retrieval syntax is ready in all respects, a
DataSet object is created, which represents the data collected from various tables. A table
represents a single set of logical data, while a data set represents scores of such tables.

The data grid has a DataSource property that is set to the DataSet c, since it is the dataset that
would finally contain the data to be displayed in the grid. The Fill function in the
SqlDataAdapter object 'cust', actually fills the DataSet object with data. In our case, an
arbitrary name of ccc is assigned to the table.

The Fill function finally commences all the activities. Using con, a connection is established
with the SQL Server. Then, the Select statement is executed to retrieve data from the database.
The data that is returned, is a list of customers retrieved from the Customers table. This data
is stored in the dataset, under the name of 'ccc'.
A DataSet is merely a collection of tables. Now, to display data from this set, the DataMember
property is initialized to the dataset object ccc. Finally, only the data-grid control is added to
the Form, using the Controls collection.

In this manner, a DataGrid control can be employed to display the data from a database.

a.vb
imports System
imports System.Data
imports System.Drawing
imports System.Windows.Forms
imports System.Data.SqlClient
imports System.Xml
public class zzz
Inherits Form
Dim d as DataGrid
dim c as DataSet
DIM con as SqlConnection
dim cust as SqlDataAdapter
dim d1,d2 as SqlDataAdapter
sub new()
d = new DataGrid()
d.Size = new Size(584, 336)
ClientSize = new Size(600, 413)
con = new SqlConnection("uid=sa;database=northwind")
cust = new SqlDataAdapter ("Select * from Customers", con)
d1 = new SqlDataAdapter ("Select * from Products", con)
d2 = new SqlDataAdapter ("Select * from Employees", con)
c = new DataSet()
d.DataSource = c
Cust.Fill(c, "ccc")
d1.Fill(c, "ddd")
d2.Fill(c, "eee")
Controls.Add(d)
end sub
shared sub Main()
dim a as zzz
a = new zzz
Application.Run(a)
end sub
end class

In the above example, two additional SqlDataAdapter objects, d1 and d2 are created. The
constructors are given the same SqlConnection object, but with different table names of
Products and Employees in the SQL statement.

Then, the Fill function is used to create two more tables called ddd and ccc, in the DataSet
object c. Thus, the DataSet object now has three tables within it.

Here, the line initializing the DataMember property, has also been deleted. On running the
above program, a window will be displayed, as shown in screen 5.2.
Screen 5.2
The only thing that is apparent in the window is a 'plus' sign, since the Data Member is not
initialized.

This sign is an indication to the DataGrid, that the + sign has to be clicked upon, in order to
display the list of tables contained in the DataSet.

Screen 5.3
Clicking on the 'plus' sign expands the DataSet to display the three tables. This is shown in
screen 5.3. We have clicked on the last table named eee. Therefore, the screen that pops up,
displays the records from the Products table, as shown in screen 5.4.
Screen 5.4
If you observe the screen very closely, you would notice an arrow marked to the left. If you click
on the arrow, you would once again land up in screen 5.3. This is how a DataGrid facilitates
switching between tables, when the DataSet comprises of more than one table.

a.vb
imports System
imports System.Data
imports System.Drawing
imports System.Windows.Forms
imports System.Data.SqlClient
imports System.Xml
public class zzz
Inherits Form
Dim WithEvents d as DataGrid
dim c as DataSet
DIM con as SqlConnection
dim cust as SqlDataAdapter
sub new()
d = new DataGrid()
d.AlternatingBackColor = Color.Red
d.BackColor = Color.Blue
d.BackgroundColor = Color.Green
d.CaptionForeColor = Color.Black
d.CaptionBackColor = Color.White
d.CaptionText = "Vijay Mukhi"
d.CaptionVisible = true
d.Size = new Size(584, 336)
ClientSize = new Size(600, 413)
con = new SqlConnection("uid=sa;database=northwind")
cust = new SqlDataAdapter ("Select * from Customers", con)
c = new DataSet()
d.DataSource = c
Cust.Fill(c, "ccc")
d.DataMember = "ccc"
Controls.Add(d)
end sub
shared sub Main()
dim a as zzz
a = new zzz
Application.Run(a)
end sub
sub abc(s as object , e as MouseEventArgs ) Handles d.MouseDown
System.Console.WriteLine("{0} {1} {2} {3}" ,e.X , e.Y , e.Clicks , e.Button)
end sub
end class

Output
177 50 1 Left
208 112 2 Right

The above DataGrid example consists of a variety of colors. First of all, the Background color is
set to green, by initializing the BackgroundColor property to Green. This property is a shared
instance member of the class Color. Next, the default BackColor of the row is set to Blue, and
the AlternatingBackColor is set to Red. Every alternate row will have the background color of
red. This completes the coloring of the grids.

The property CaptionText determines the title of the data grid. Here, it is set to "Vijay Mukhi".
The caption is made visible, and the foreground and background colors are initialized to Black
and White, respectively. This is achieved by setting the CaptionForeColor and
CaptionBackColor properties in the datagrid.

The most striking feature of this example is not the setting of the myriad colors, but the 'event
handling' of the grid. Since we want the DataGrid object 'd' to handle events, the WithEvents
keyword is specifed with the handler assigned to sub abc, with the object event of
d.MouseDown.

This initialization traps every mouseclick on the grid and calls the function abc. The sub abc is
called with two parameters, where the second parameter 'e' is of type MouseEventArgs. This
object has two properties named X and Y, which store the position of the mouse click. The
Clicks property maintains the count of the total number of clicks. Finally, the button property
apprizes us of whether it was the Right mouse button that was clicked, or was it the Left
mouse button to be clicked.

The output discloses that it was the Right mouse button that was clicked twice, at x and y of
208 and 112. By implementing this feature, we can assist in monitoring the actions of the user
on the data grid.

a.vb
imports System
imports System.Data
imports System.Drawing
imports System.Windows.Forms
imports System.Data.SqlClient
imports System.Xml
public class zzz
Inherits Form
Dim WithEvents d as DataGrid
dim c as DataSet
DIM con as SqlConnection
dim cust as SqlDataAdapter
sub new()
d = new DataGrid()
d.Size = new Size(584, 336)
ClientSize = new Size(600, 413)
con = new SqlConnection("uid=sa;database=northwind")
cust = new SqlDataAdapter ("Select * from Customers", con)
c = new DataSet()
d.DataSource = c
Cust.Fill(c, "ccc")
d.DataMember = "ccc"
Controls.Add(d)
end sub
shared sub Main()
dim a as zzz
a = new zzz
Application.Run(a)
end sub
sub abc(s as object , e as EventArgs ) handles d.CurrentCellChanged
Dim g as DataGridCell
g = d.CurrentCell
dim c,r as integer
c = g.ColumnNumber
r = g.RowNumber
System.Console.WriteLine( "Column {0} Row {1} Value {2}" , c , r , d(r,c))
d(r,c) = "hi " & d(r,c)
end sub
end class

Output
Column 3 Row 2 Value Owner
Column 2 Row 4 Value Christina Berglund

A DataGrid object is comprised of multiple objects of type DataGridCell. These objects are the
cells which are at the intersection of a row and a column storing data. Whenever we click on a
cell to change its value, the event CurrentCellChanged gets called. In our program, we have
trapped this event to call the function abc.

The CurrentCell property returns a DataGridCell object, which represents the cell that the user
has currently clicked on. This value is stored in the variable 'g'. Thus, each time a cell is
clicked on, the value of the CurrentCell property is updated, to reflect this new cell or
DataGridCell object.

The DataGridCell object has two properties, named ColumnNumber and RowNumber, which
return integer values representing the column and the row that have been selected. These
values are printed with the help of the WriteLine function, along with the indexer that accepts a
row and column number. The indexer returns the contents of the Cell. To change the current
contents of the cell, the indexer can be reused, with the new value that is to be assigned.

In Visual Basic.Net, the & sign is utilized to concatenate or join two strings. The indexer for the
DataGrid class is 'read-write'. Hence, it can be used to get a value, as well as, to set a value.

a.vb
imports System
imports System.Data
imports System.Drawing
imports System.Windows.Forms
imports System.Data.SqlClient
imports System.Xml
public class zzz
Inherits Form
Dim d as DataGrid
dim t as DataTable
Dim r as DataRow
sub new()
d = new DataGrid()
d.Size = new Size(584, 336)
ClientSize = new Size(600, 413)
t = new DataTable()
Dim c as DataColumnCollection
c = t.Columns
c.Add("Name")
c.Add("City")
r = t.NewRow()
r("Name") = "Vijay"
r("City") = "Bombay"
Dim r1 as DataRowCollection
r1 = t.Rows
r1.Add(r)
r = t.NewRow()
r("Name") = "Sonal"
r("City") = "Delhi"
t.Rows.Add(r)
d.DataSource = t
Controls.Add(d)
end sub
shared sub Main()
dim a as zzz
a = new zzz
Application.Run(a)
end sub
end class

Screen 5.5
The above example demonstrates the fact, that the data displayed in a data grid may not
necessarily originate from a table in a database. It could originate from anywhere. The screen
5.5 authenticates this fact.

To implement the above, a DataTable object t is created. The constructor may be passed the
name of the table Control, however, this is optional.
A table is made up of a large number of columns or DataColumn objects, which are
represented by a DataColumnCollection object. The DataTable has a member called Columns,
which returns a DataColumnCollection. Using the Add function in the DataColumnCollection
class, two columns named Name and City are added to the data table.

Thus, while dealing with a data table object, the basic principle to be kept in mind is that the
properties of the DataTable are to be used, and not the DataTable object itself.

Applying the same principle, records are added to the data table. The NewRow function adds a
new empty row to the DataTable object. Then, using the indexer and assigning it a parameter
of the column name, the columns are individually assigned to some values.

After having initialized the DataRow object, the row now has to be added to the DataTable. The
DataRows are stored in a DataRowCollection object, which can be accessed using the Rows
collection. So, like before, we use the 'Add' function to add the row to the DataTable. In all, two
rows are added to the DataTable. Thus, a DataTable is merely a collection of records, and it
represents records in the same manner in which they are stored in the database.

While retrieving data from the table, the DataSource property was initialized to a data set.
However, when values are assigned individually, a DataTable object is used. Thus, the data
grid object is flexible enough to work with different data sources.

a.vb
imports System
imports System.Data
imports System.Drawing
imports System.Windows.Forms
imports System.Data.SqlClient
imports System.Xml
public class zzz
Inherits Form
Dim d as DataGrid
dim c as ccc
DIM con as SqlConnection
dim cust , ord as SqlDataAdapter
sub new()
d = new DataGrid()
d.Size = new Size(584, 336)
ClientSize = new Size(600, 413)
con = new SqlConnection("uid=sa;database=northwind")
cust = new SqlDataAdapter ("Select * from Customers", con)
Ord = new SqlDataAdapter ("Select * from Orders", con)
c = new ccc
d.DataSource = c
d.DataMember = "Customers"
Cust.Fill(c, "Customers")
Ord.Fill(c, "Orders")
Controls.Add(d)
end sub
shared sub Main()
dim a as zzz
a = new zzz
Application.Run(a)
end sub
end class
public class ccc
Inherits DataSet
dim tc as cus
dim to1 as orders
dim r as DataRelation
public sub new
tc= new cus("Customers")
Tables.Add(tc)
to1= new Orders("Orders")
Tables.Add(to1)
r = new DataRelation("custord",tc.cID,to1.oID)
Relations.Add(r)
end sub
end class
public class cus
Inherits DataTable
public dim cID as DataColumn
sub new (n as string)
Mybase.New(n)
cID = new DataColumn("CustomerID")
Columns.Add(cID)
end sub
end class
public class Orders
Inherits DataTable
public oID as DataColumn
sub new (n as string)
mybase.new(n)
oID = new DataColumn("CustomerID")
Columns.Add(oID)
end sub
end class

The explanation to the above program could run into reams of paper. Therefore, we shall
examine it only after executing the program.

But prior to that, we would like to furnish one crucial bit of information to you, i.e. the
Customer table contains a list of customers, while the Orders table contains the list of orders
that the customer places on a regular basis.

The output displays a list of customers, with a plus sign placed next to it. This is displayed in
screen 5.6.
Screen 5.6
If we click on the plus sign, custord will be displayed with a link, as shown in the screen 5.7.

Screen 5.7
When we click on this link, a list of orders placed by the customer will be displayed. Screen 5.8
substantiates this point.

Screen 5.8
The above procedure can be repeated for any customer, whose orders need to be viewed. This
example demonstrates how data from different data tables, can now be related to each other.

Most of the code remains unchanged, with the exception of a few additions. An additional
SqlDataAdapter object named ord is created, to represent records from the Orders table. The
major difference is that the class ccc, which is derived from the DataSet class, is employed in
lieu of using the DataSet class directly.

A significant advantage of this approach is that, the code that is deemed appropriate from the
DataSet class, can be utilized; whereas, the code that is not considered acceptable, can be
overridden.

Thus, at the very outset, we have created a ccc object. This leads to a call to the constructor of
the ccc class. In the ccc object, three instance objects have been created.

The first one is of type class cus, which is derived from the DataTable class. The cus
constructor is passed the name of the table, i.e. Customers, which would be considered a little
later, along with the Fill function. Both the names should necessarily be the same, or else, an
exception would be thrown.

Since the cus class is derived from the DataTable class, it represents some data. By overriding
the DataTable class, an additional column can be added to the DataTable. This is
accomplished by creating a DataColumn called CustomerID, and then, by using the add
function to add the column to the Columns collection. Candidly speaking, the sole purpose of
deriving the cus class from the DataTable class is to add a column named CustomerID.

The column CustomerID is visible in both the tables, i.e. Customers and Orders. Hence, it can
be used to relate records in these two tables. In the ccc class, the class cus is added to the list
of DataTables, maintained by the Tables collection of the DataSet class.

Next, we create an object that is an instance of the Orders class. The mechanism of this object
remains analogous to the cus class, where a new column named CustomerID is introduced.
The DataColumn object in both the DataTable classes is an instance variable, and hence, it is
accessible to the other objects.

Now, we descend upon the most crucial lines of the code.

An object of class DataRelation is created, where the first parameter to the constructor is a
string, denoting the name of the DataRelation. The string assigned here is custord. As an
outcome of this, the hyperlink of custord is revealed when the plus sign is clicked upon.

The next parameter is the DataColumn for the Primary key, which is the column that is
unique. The value assigned in the Customers table is CustomerID, since it is unique for every
customer. The last parameter is the Foreign Key in the second table, which contains multiple
values of the Primary Key. The CustomerID column in the Orders table has more than one
occurrence of the Customer id.

Using the Add function, the relation is then added to the Relations object. This is indicative of
the fact, that there may be many more relations that can be added, once the relation between
the data has been established. The rest of the code remains unaltered. Both, the primary key
and the foreign key need to be represented by DataColumn objects. Therefore, the two classes
of cus and orders have been created.
Having acquired the knowledge of the concepts, by manually creating an application, let us
now allow Visual Studio.Net to create it for us. The application will consist of a Form having a
button and a DataGrid. Once the user clicks on the button, the DataGrid will be suffused with
data from the Database.

What is of prime significance here is that, all this transpires without our having to write reams
of code, since it is the framework that handles this task. However, after discerning as to how
the application works, we shall venture forth to unravel the code written by Visual Studio.Net.

We would implore you not to meddle with the application, till we have accomplished our task.
We have endeavoured to subsume most of the screens for your convenience, thus eliminating
the need to sit in front of the computer while browsing the chapter.

In Visual Studio.Net, you can create a new project by clicking on the menu, File - New - Project.
This opens up the New Project dialog box. In the first pane, choose Visual Basic Projects, and
in the second pane, choose Windows Applications. Enter t1 as the name of the project, and
point the directory to c:\v1. Then, click on OK. You may wish to select the project name;
however, we would request you to initially accept our names as they are.

You should arrive at screen 5.9, having a mammoth Form and a ToolBox to the left. If for some
reason, you are unable to see the toolbox on the left, click on the menu View, and select
ToolBox. But, if you have toolbox that hides automatically, unselect the option of AutoHide
from the Windows menu option.

Screen 5.9
The toolbox has millions of controls. Hence, they are located under separate categories or tabs.
The category of Data encompasses all the controls that work with data. Since this chapter
deals with databases, select the Data subhead, and examine the controls stipulated below it.
The screen that we arrive at, is shown in screen 5.10.
Screen 5.10
Choose the control called OleDbDataAdapter and place it on the Form. It should be done in a
manner akin to the one employed in the previous chapter, to place a button on the Form.

The procedure remains the same, i.e. click on the control to select it, and then, keeping the left
mouse button pressed, drag and drop it onto the Form. Surprisingly, unlike a button, the
control is not positioned on the Form. Instead, a dialog box pops up, as shown in screen 5.11.

Screen 5.11
Here, we are informed in no uncertain terms, that we are in the august presence of Merlin's
Wizard! The job of a wizard is to make our lives easier by using black magic. Thus, a Microsoft
wizard throws a volley of questions at us. Based on the answers furnished by us, it toils
towards ameliorating our lives by writing tons of code to achieve the desired outcome.
By convention, the first screen of a wizard provides information about the type of the wizard.
Presently, we are not very keen on reading it. So, we click on the Next button. This transports
us to the next step in the wizard, as shown in screen 5.12.

Screen 5.12
Here, we are asked to confirm which database we prefer to connect to. Since we have not
created any connection in the past, we click on the 'New connection' button to arrive at screen
4.13.

Screen 5.13
The Data Link Properties Dialog Box requires considerable amount of information, before it can
create a new connection. However, all the boxes do not require to be filled up. The server name
is left blank, since the database server runs on the same machine as the application.
A connection is made to the server, employing the user name 'sa'. Therefore, 'sa' is entered in
the textbox labeled 'User Name'. Since the password is set to blank, the Check box for Blank
password is selected. Then, we click on the down arrow of the drop down listbox for displaying
the option of select database on server. The screen 5.14 depicts the outcome of our action.

Screen 5.14
The list provided is that of the databases, which have been created on the server. After having
selected the Northwind database, the dialog box looks similar to what is shown in screen 5.15.

Screen 5.15
The program that we had written earlier had incorporated data from the 'Northwind' database,
which is visible in the list. Although we are at liberty to select the database, we will stick to
Northwind for the time being.
An added advantage of a drop down listbox is that, it averts the possibility of a wrong database
name being entered by the user, since a name has to be selected from the list that is provided.

Now, click on the button labeled Test Connection. This finally ascertains whether all the values
entered are valid from the standpoint of the database server.

The Screen 5.16 returns with a message of success.

Screen 5.16
Some processing had taken place when the Test connection button was clicked.

This involved connecting to SQL Server using the user name 'sa' and using a blank password,
and thereafter, accessing the Northwind database.

When you click on OK, you will be reverted back to one of the previous screens, with the name
of VMUKHI.Northwind.dbo in the listbox, as shown in screen 5.17.
Screen 5.17
Vmukhi is the name assigned to our computer. It is followed by the name of the database
Northwind, with an extension of dbo.

Now, click on the Next button to arrive at the screen 5.18. A 'stored procedure' is a program
that resides and executes on the server. We feel quite contented to use the trusted SQL Select
statements to fetch data. So, we simply click on the Next button, without making any
amendments to screen 5.18.

Screen 5.18
In screen 5.19, we see a cursor blinking in the text area, which is an indication that an SQL
Select statement has to be entered.
Screen 5.19
In case you are not conversant with the options, you can click on Query Builder, as we have
done, to arrive at screen 5.20.

Screen 5.20
The listbox displays the tables that constitute the Northwind database. Click on Customers,
and then on the Add button. The background area in screen 5.21 changes, to display a box
containing the word 'customers', with a list of fields within it. Since there are no more tables to
select from, we click on the Close button.
Screen 5.21
In screen 5.22, the first three fields are selected. This has been achieved by clicking on the
check box, due to which, the selected field names get added to the Select statement.

Screen 5.22
Since we are satisfied with what we have achieved, we click on the OK button.

This takes us to screen 5.23, which displays the Select statement that is generated. The Select
statement shows the three selected fields of CustomerID, Company Name and ContactName for
which data is to be retrieved from the Customers table.
Screen 5.23
This is where a wizard really flaunts its mettle, since it is easier to select from a list, rather
than to write things out manually. The beauty of SQL is revealed in all its glory, when we have
to work with multiple tables.

When we click on the Next button, the screen 5.24 reveals that the wizard has internally
performed a number of tasks for us. Finally, click on Finish to end the wizard.

Screen 5.24
As shown in screen 5.25, the screen does not show the object on the Form; instead, it has two
objects, i.e. OleDbDataAdapter1 and OleDbConnection1, which are located at the bottom of the
screen.
Screen 5.25
This highlights the fact that there are two types of controls.
• The first type is similar to a button, which gets displayed in the Form.
• The second type is that of OleDbConnection, which does not have an User Interface,
and which gets placed at the bottom of the Form.

The next object required is a DataSet object, which simply relates to a collection of tables. The
Visual Studio.Net interface has a menu option Data. So, click on Data and select the option of
'Generate Dataset', as shown in Screen 5.26.

Screen 5.26
This generates a screen, as shown in screen 5.27.
Screen 5.27
We are contented with the default options assigned to our dataset, where the name indicated is
DataSet1. The framework is extremely astute, and hence, it adds the Customers table to the
Dataset, as shown in the screen. Finally, since the check box at the bottom is selected, the
dataset gets added to the Designer.

When the OK button is clicked, the dataset DataSet1 will be displayed at the bottom of the non
User Interface controls. This is shown in Screen 5.28

Screen 5.28
Next, a DataGrid has to be added to the Form. So, we click on the Windows Form Tab in the
toolbox, select DataGrid and drag it onto the Form window. This is shown in Screen 5.29 and
Screen 5.30.
Screen 5.29

Screen 5.30
The DataGrid and Form are disconcertingly cramped. So, we click on their edges and drag
them diagonally to enlarge their sizes, as in evident in screen 5.31.
Screen 5.31
Our program had initialized various properties such as ClientSize, Background color etc. To
modify these properties, click on the menu option of View-Properties Window.

Screen 5.32
This brings up the Properties window on the right hand side of the screen. Since the DataGrid
control is selected, the properties related to the Grid are displayed. When you select the Form,
its properties get displayed in the Properties Window.

Click on the drop down listbox for the DataSource property. It displays a list containing two
items, as seen in screen 5.33. Select the option of DataSet1, as shown in screen 5.34.
Screen 5.33

Screen 5.34
The DataMember property that is positioned immediately above the DataSource, is also
required to be Set. So, click on the down arrow, and from the list, select the table named
Customers.
Screen 5.35
In screen 5.36, we have selected the Customers table. On doing so, the DataGrid automatically
gets transformed to display three fields in the table.

Screen 5.36
Now, incorporate another button control into the Form, as is shown in screen 5.37. The
Properties window exhibits the properties for the button, since it is the selected object.
Screen 5.37
In the Properties window, enter the word 'Load' in the Text property of the button. Then, press
Enter, and you will instantly witness the text on the button change to 'Load', as shown in
screen 5.38.

Screen 5.38
Now, double click on the button, and enter the following line at the cursor position:

OleDbDataAdapter1.Fill(DataSet11)
Screen 5.39
In order to verify whether everything has been entered accurately or not, click on the menu
option Debug, and then, click on Start. The screen that emerges is shown in screen 5.40.

Screen 5.40
An empty DataGrid and a button labeled 'Load' are displayed. Now, click on the button. Lo &
behold! The screen transforms, displaying the Data Grid containing the data retrieved from the
Customer table, as is evident in screen 5.41.
Screen 5.41
Let us now snoop behind the scenes to check on the activities that have taken place. We would
be explaining each new step incorporated above, by interpreting the code that has been
generated. You will never be able to grasp the applications without learning the language.
Therefore, we insist that you learn the language before diving straight into the depths of the
applications. We have copied the generated code and presented it below

We have converged our attention around the code that has been introduced recently, so that
our explanation is confined to the newly added code only, in order to avoid repetition.
Furthermore, once the code has been explicated, it will not be exhibited again.

Since the process of code generation has been automated, a lot of code has been included to
handle situations that may possibly occur only once in a million years! Thus, a lot of
redundant code is generated. We have displaced all comments, since they impede our
understanding of the code. We have also done away with all the blank lines, since they occupy
too much space.
Public Class Form1
Inherits System.Windows.Forms.Form
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
InitializeComponent()
End Sub
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
Friend WithEvents OleDbDataAdapter1 As System.Data.OleDb.OleDbDataAdapter
Friend WithEvents OleDbSelectCommand1 As System.Data.OleDb.OleDbCommand
Friend WithEvents OleDbInsertCommand1 As System.Data.OleDb.OleDbCommand
Friend WithEvents OleDbUpdateCommand1 As System.Data.OleDb.OleDbCommand
Friend WithEvents OleDbDeleteCommand1 As System.Data.OleDb.OleDbCommand
Friend WithEvents OleDbConnection1 As System.Data.OleDb.OleDbConnection
Private components As System.ComponentModel.IContainer
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.OleDbDataAdapter1 = New System.Data.OleDb.OleDbDataAdapter()
Me.OleDbSelectCommand1 = New System.Data.OleDb.OleDbCommand()
Me.OleDbInsertCommand1 = New System.Data.OleDb.OleDbCommand()
Me.OleDbUpdateCommand1 = New System.Data.OleDb.OleDbCommand()
Me.OleDbDeleteCommand1 = New System.Data.OleDb.OleDbCommand()
Me.OleDbConnection1 = New System.Data.OleDb.OleDbConnection()
Me.OleDbDataAdapter1.DeleteCommand = Me.OleDbDeleteCommand1
Me.OleDbDataAdapter1.InsertCommand = Me.OleDbInsertCommand1
Me.OleDbDataAdapter1.SelectCommand = Me.OleDbSelectCommand1
Me.OleDbDataAdapter1.TableMappings.AddRange(New
System.Data.Common.DataTableMapping() {New
System.Data.Common.DataTableMapping("Table", "Customers", New
System.Data.Common.DataColumnMapping() {New
System.Data.Common.DataColumnMapping("CustomerID", "CustomerID"), New
System.Data.Common.DataColumnMapping("CompanyName", "CompanyName"), New
System.Data.Common.DataColumnMapping("ContactName", "ContactName")})})
Me.OleDbDataAdapter1.UpdateCommand = Me.OleDbUpdateCommand1
Me.OleDbSelectCommand1.CommandText = "SELECT CustomerID, CompanyName, ContactName
FROM Customers"
Me.OleDbSelectCommand1.Connection = Me.OleDbConnection1
Me.OleDbInsertCommand1.CommandText = "INSERT INTO Customers(CustomerID,
CompanyName, ContactName) VALUES (?, ?, ?); SEL" & _
"ECT CustomerID, CompanyName, ContactName FROM Customers WHERE (CustomerID = ?)"
Me.OleDbInsertCommand1.Connection = Me.OleDbConnection1
Me.OleDbInsertCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("CustomerID", System.Data.OleDb.OleDbType.VarWChar,
5, "CustomerID"))
Me.OleDbInsertCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("CompanyName",
System.Data.OleDb.OleDbType.VarWChar, 40, "CompanyName"))
Me.OleDbInsertCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("ContactName", System.Data.OleDb.OleDbType.VarWChar,
30, "ContactName"))
Me.OleDbInsertCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("Select_CustomerID",
System.Data.OleDb.OleDbType.VarWChar, 5, "CustomerID"))
Me.OleDbUpdateCommand1.CommandText = "UPDATE Customers SET CustomerID = ?,
CompanyName = ?, ContactName = ? WHERE (Cust" & _
"omerID = ?) AND (CompanyName = ?) AND (ContactName = ? OR ? IS NULL AND ContactN" & _
"ame IS NULL); SELECT CustomerID, CompanyName, ContactName FROM Customers WHERE (" &
_
"CustomerID = ?)"
Me.OleDbUpdateCommand1.Connection = Me.OleDbConnection1
Me.OleDbUpdateCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("CustomerID", System.Data.OleDb.OleDbType.VarWChar,
5, "CustomerID"))
Me.OleDbUpdateCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("CompanyName",
System.Data.OleDb.OleDbType.VarWChar, 40, "CompanyName"))
Me.OleDbUpdateCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("ContactName", System.Data.OleDb.OleDbType.VarWChar,
30, "ContactName"))
Me.OleDbUpdateCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("Original_CustomerID",
System.Data.OleDb.OleDbType.VarWChar, 5, System.Data.ParameterDirection.Input, False,
CType(0, Byte), CType(0, Byte), "CustomerID", System.Data.DataRowVersion.Original, Nothing))
Me.OleDbUpdateCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("Original_CompanyName",
System.Data.OleDb.OleDbType.VarWChar, 40, System.Data.ParameterDirection.Input, False,
CType(0, Byte), CType(0, Byte), "CompanyName", System.Data.DataRowVersion.Original,
Nothing))
Me.OleDbUpdateCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("Original_ContactName",
System.Data.OleDb.OleDbType.VarWChar, 30, System.Data.ParameterDirection.Input, False,
CType(0, Byte), CType(0, Byte), "ContactName", System.Data.DataRowVersion.Original,
Nothing))
Me.OleDbUpdateCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("Original_ContactName1",
System.Data.OleDb.OleDbType.VarWChar, 30, System.Data.ParameterDirection.Input, False,
CType(0, Byte), CType(0, Byte), "ContactName", System.Data.DataRowVersion.Original,
Nothing))
Me.OleDbUpdateCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("Select_CustomerID",
System.Data.OleDb.OleDbType.VarWChar, 5, "CustomerID"))
Me.OleDbDeleteCommand1.CommandText = "DELETE FROM Customers WHERE (CustomerID = ?)
AND (CompanyName = ?) AND (ContactNa" & _
"me = ? OR ? IS NULL AND ContactName IS NULL)"
Me.OleDbDeleteCommand1.Connection = Me.OleDbConnection1
Me.OleDbDeleteCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("Original_CustomerID",
System.Data.OleDb.OleDbType.VarWChar, 5, System.Data.ParameterDirection.Input, False,
CType(0, Byte), CType(0, Byte), "CustomerID", System.Data.DataRowVersion.Original, Nothing))
Me.OleDbDeleteCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("Original_CompanyName",
System.Data.OleDb.OleDbType.VarWChar, 40, System.Data.ParameterDirection.Input, False,
CType(0, Byte), CType(0, Byte), "CompanyName", System.Data.DataRowVersion.Original,
Nothing))
Me.OleDbDeleteCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("Original_ContactName",
System.Data.OleDb.OleDbType.VarWChar, 30, System.Data.ParameterDirection.Input, False,
CType(0, Byte), CType(0, Byte), "ContactName", System.Data.DataRowVersion.Original,
Nothing))
Me.OleDbDeleteCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("Original_ContactName1",
System.Data.OleDb.OleDbType.VarWChar, 30, System.Data.ParameterDirection.Input, False,
CType(0, Byte), CType(0, Byte), "ContactName", System.Data.DataRowVersion.Original,
Nothing))
Me.OleDbConnection1.ConnectionString = "Provider=SQLOLEDB.1;Persist Security
Info=False;User ID=sa;Initial Catalog=Northw" & _
"ind;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation" & _
" ID=VMUKHI;Use Encryption for Data=False;Tag with column collation when poss" & _
"ible=False"
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(292, 273)
Me.Name = "Form1"
Me.Text = "Form1"
End Sub
#End Region
End Class

In the Form, we first introduced an OleDbDataAdapter object. This resulted in the creation of
the DataAdapter and DataConnection objects, which carried the information essential to
connect to the database server. Resultantly, the above code was generated.

Other than calling the InitializeComponent function, the constructor has no other role to play.
All the valuable code is placed in the InitializeComponent function. The Dispose function
remains unchanged, and can be ignored completely. Normally, all the instance variables are
positioned after these housekeeping functions.

We had earlier learnt that two objects of type SqlConnection and SqlDataAdapter were required
to connect to any database in SQL Server. Since these objects are restricted to SQL Server,
they cannot be used in the context of other database servers.

Thus, the code is not generic in nature. Therefore, the Wizard creates objects of type
OleDbConnection and OleDbDataAdapter, since they can then be implemented on multiple
database servers, including SQL Server. Barring this difference, both the objects work in a
similar manner.

Every corresponding object has a number appended to the name of the class. This number gets
incremented when another object of the same kind is added to the Form. This helps in
assigning unique names to each object.

The wizard generates SQL statements for adding, updating and deleting records from the
database. The OleDbCommand object is created for this purpose, since it is conversant in
dealing with SQL.

In the InitializeComponent function, all the instance variables are initialized using the new
keyword with the constructor that does not take any parameters.

The DeleteCommand is a property of type OleDbCommand, which represents an SQL


statement or a stored procedure employed for deleting records. Similarly, the object also
contains properties to represent Insert, Select and Update commands.

The property of TableMappings is of type DataTableMappingCollection. It is a collection objects


that provides the master mapping between the source table and a DataTable. The AddRange
function accepts an array of DataTableMapping objects.

Me.OleDbDataAdapter1.TableMappings.AddRange(New
System.Data.Common.DataTableMapping() {New
System.Data.Common.DataTableMapping("Table", "Customers", New
System.Data.Common.DataColumnMapping() {New
System.Data.Common.DataColumnMapping("CustomerID", "CustomerID"), New
System.Data.Common.DataColumnMapping("CompanyName", "CompanyName"), New
System.Data.Common.DataColumnMapping("ContactName", "ContactName")})})

The first parameter to the constructor of a DataTable Mapping is a string that represents a
table. The second parameter is the name of the table selected in the Select statement, i.e.
Customers. Following this array, there is another array named Columns, which is a
DataColumnMapping collection.

The DataColumnMapping object takes two strings:


• The first is the column name from the data source.
• The second is the column name from the DataSet that it must map to.

While building the query, we had chosen three columns. Therefore, there are three members in
the array. The parameter names and the column names have been kept identical.

Me.OleDbSelectCommand1.CommandText = "SELECT CustomerID, CompanyName, ContactName


FROM Customers"

Do bear in mind that most of the above code is superfluous, and hence, dispensable. The
OleDbCommand has a property called CommandText, using which, the OleDBSelectCommand
is initialized to the SQL statement associated with the Command object. The SQL statement
that is generated with the Query Builder, is assigned to this property.

Me.OleDbSelectCommand1.Connection = Me.OleDbConnection1

Every Adapter object needs a Connection handle. Therefore, the Connection property is set to
the OleDbConnection object.

The CommandText property in the Insert Object is set to a SQL Insert statement. We can very
easily add records while the DataGrid is displaying its set of records.

Me.OleDbInsertCommand1.CommandText = "INSERT INTO Customers(CustomerID,


CompanyName, ContactName) VALUES (?, ?, ?); SEL" & _
"ECT CustomerID, CompanyName, ContactName FROM Customers WHERE (CustomerID = ?)"

Note, that the syntax for the Insert statement starts with the reserved words 'Insert into',
followed by the name of the table, i.e. Customers, followed by the list of field names in round
brackets. This is followed by the reserved word 'values', which is followed by the actual values
that need to be inserted, placed within round brackets. These values are not known at this
stage. Hence, they are represented by a ? symbol, which represents a parameter or a place
holder. It will be substituted by a value in due course of time.

The SELECT statement with the Insert command identifies a unique customer record. Since
the line is broken on two lines, the word ‘SELECT’ has the continuation character of &_ in
double quotes.
One line below the insert command is the Parameters Collection object, which keeps track of
all the parameters.

Me.OleDbInsertCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("CustomerID", System.Data.OleDb.OleDbType.VarWChar,
5, "CustomerID"))

To this collection, we add an instance of an OleDbParameter object, whose constructor takes


the following parameters: First is a parameter name, followed by the data type, followed by the
width of the column, and finally, followed by the name of the source column. The parameters
have been assigned the same names as those of the fields.

Me.OleDbInsertCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("Select_CustomerID",
System.Data.OleDb.OleDbType.VarWChar, 5, "CustomerID"))
The parameter that identifies the CustomerID in the where clause is given the name of
Select_CustonerID as shown above.

Me.OleDbUpdateCommand1.CommandText = "UPDATE Customers SET CustomerID = ?,


CompanyName = ?, ContactName = ? WHERE (C" & _
"ustomerID = ?) AND (CompanyName = ?) AND (ContactName = ? OR ? IS NULL AND ContactN" & _
"ame IS NULL); SELECT CustomerID, CompanyName, ContactName FROM Customers WHERE (" &
_
"CustomerID = ?)"

The Update command follows next, which contains the word Update, followed by the table
name Customers, and finally, followed by the fields that are to be changed.

Only three of the fields have been chosen. Therefore, the SET command has exactly three field
names as three parameters. By default, the Update statement acts on all records. Therefore, to
modify only specific records, the 'where' clause has to be used, thereby restricting access to the
number of records it can act upon.
The CustomerID is the primary key, which has a unique value for each record. Therefore, if the
Primary key is used in the 'where' clause, it will affect only one record.

Me.OleDbUpdateCommand1.Parameters.Add(New
System.Data.OleDb.OleDbParameter("Original_CustomerID",
System.Data.OleDb.OleDbType.VarWChar, 5, System.Data.ParameterDirection.Input, False,
CType(0, Byte), CType(0, Byte), "CustomerID", System.Data.DataRowVersion.Original, Nothing))

If you notice the parameter statement, the name of the field has been prefixed with the word
Original_. The fourth parameter is an enum of ParameterDirection, which refers to the type of
parameter. The enum consists of the four values of Input, Input Output, Output and Return
Value.

The value of Input signifies that the value has not been received, but would be furnished in due
course. The next value of False is a Boolean, which indicates whether the column will accept
null values or not; a value of True signifies it that it will.

The next value is for precision, which determines the number of digits permissible to the left
and right of the decimal point. This is followed by the Scale parameter, which determines the
total number of decimal places that the field can be resolved to. Initially the above two
parameters would appear absurd.

Next, we encounter the real column name, followed by a DataRowVersion enum, which takes
four values: Current, Default, Original and Proposed. Original represents original values, as
against the current values. Finally, we come to an object, which currently has no value.

Me.OleDbDeleteCommand1.CommandText = "DELETE FROM Customers WHERE (CustomerID = ?)


AND (CompanyName = ?) AND (ContactName = ? OR ? IS NULL AND ContactName IS NULL)"

After the Update command, we shall tackle the Delete command. This command starts with the
words 'delete from', followed by the table name Customers, and then, followed by the 'where'
clause that identifies the records that can be deleted. The parameters are identical to the
Update command.

Me.OleDbConnection1.ConnectionString = "Provider=SQLOLEDB.1;Persist Security


Info=False;User ID=sa;Initial Catalog=Northw" & _
"ind;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation" & _
" ID=VMUKHI;Use Encryption for Data=False;Tag with column collation when poss" & _
"ible=False"

Now, the focus once again shifts to the OleDbConnection object. Earlier, we had passed the
connection string to the constructor. However, as an alternative approach, we could use the
ConenctionString property instead. This is what the Constructor eventually does with the
string that is passed to it.

We shall restrict ourselves to only a few of the connection string properties for the moment.
When the property Provider refers to the database, a connection gets established. The User ID
property is the username, and the Initial Catalog is Northwind. The rest of the code is
conventional and mundane.

Before concluding its tasks, the wizard takes all the information that has been entered in the
textboxes, and it generates the above code. After the wizard generated the SQL statements, we
clicked on the menu-option Generate Dataset under Data.

Friend WithEvents DataSet11 As t1.DataSet1

The dataset name DataSet1, which is the default name, is used to identify the dataset. This
results in the generation of an instance variable called DataSet11 of type t1. Its name is formed
by joining the name of our project with the name of the dataset, i.e. DataSet1.

Before proceeding any further, click on the menu option View, and then, on the Solution
Explorer. This brings us to screen 5.42, which has a window that lists the files constituting our
project.

Screen 5.42
A Solution or a project for the moment will be used interchangeably.

The file Form1.vb contains the code for the Form Design. Double click on the DataSet1.xsd file,
which is generated on creation of the DataSet object. This brings us to the screen 5.43, where
the relationship has been depicted in a visual form.
Screen 5.43
In the Solution Explorer, right click on Form1.vb item. The popup menu that emerges is shown
in screen 5.44. It reveals the options that are available on this item.

Screen 5.44
On selecting View Code, we arrive at the Code Generator. The other route to reach the Code
Generator is by clicking on the filename Form1.vb on the panel, just below the toolbars.

In the code that is generated within Form1.vb, we place the cursor on the line DataSet11 at
t1.DataSet1, and then, right click on it. Select the option of 'Go to definitions', as shown in
screen 5.45.
Screen 5.45
This option would transport you to screen 5.46, where the class of DataSet1 is defined. It
divulges the fact that DataSet1 is defined in a separate class of DataSet1.vb, and it is derived
from the class DataSet.

Screen 5.46
In the beginning of the file DataSet1.vb, we come across the word Autogenerated, which merely
informs us that a program has generated this file. Someday we intend to lift the veil off the
name of the program that generates this code, and also the code that it generates. However, at
this moment, we are not interested in the code that is written for the DataSet, since we shall
not be using any of it.
The Visual Basic.Net language assimilates various types of code, which we shall abstain from
analyzing right now. However, we shall explicate the various types of code, as we stumble upon
them in due course.

In addition to the instance variable that gets created, the following lines get added to the
Form.vb code, when the Generate DataSet menu-option is selected.

Me.DataSet11 = New t1.DataSet1()


CType(Me.DataSet11, System.ComponentModel.ISupportInitialize). BeginInit()
Me.DataSet11.DataSetName = "DataSet1"
Me.DataSet11.Locale = New System.Globalization.CultureInfo("en-US")
Me.DataSet11.Namespace = "http://www.tempuri.org/DataSet1.xsd"
CType(Me.DataSet11, System.ComponentModel.ISupportInitialize).EndInit()

The first line simply creates an instance of a DataSet1 object and stores it in DataSet11. The
next line begins with a function called Ctype.

The sole task of the Ctype function is to convert the data type of the first parameter, to the data
type specified by the second parameter. Thus, the first parameter, i.e. DataSet11 object of type
DataSet is converted into the ISupportInitialize type, which belongs to the
System.ComponentModel namespace.

The resultant object is an ISupportInitialize type, from which the BeginInit function is called.
The above line could have been alternately worded as: DataSet11.BeginInit(). This is so
because the DataSet class is derived from ISupportInitialize.

The BeginInit function signals to the framework that the DataSet initialization process has
commenced, and hence, it should stop all activities related to the DataSet, until the EndInit
function gets called. Therefore, the DataSet object is not authorized to carry out any internal
initializations, since these functions optimize changes to multiple sets of properties.

However, at design time, we can co-initialize properties that are contingent upon each other.
For example, the DataSetName specifies a name for the DataSet, and the locale always deals
with the language issues. The namespace property handles the reading and writing of XML
Schemas. A point to be noted here is that, the above code is spread out in the sub
InitializeComponent.

After the DataSet generation is completed, we had selected the DataGrid control from the
Forms Toolbox. This results in the creation of an instance variable called DataGrid1.

Friend WithEvents DataGrid1 As System.Windows.Forms.DataGrid


Me.DataGrid1 = New System.Windows.Forms.DataGrid()

CType(Me.DataGrid1,System.ComponentModel.ISupportInitialize). BeginInit()

Me.DataGrid1.DataMember = ""
Me.DataGrid1.HeaderForeColor = System.Drawing.SystemColors.ControlText
Me.DataGrid1.Location = New System.Drawing.Point(32, 64)
Me.DataGrid1.Name = "DataGrid1"
Me.DataGrid1.TabIndex = 0

Me.Controls.AddRange(New System.Windows.Forms.Control() {Me.DataGrid1})


CType(Me.DataGrid1, System.ComponentModel.ISupportInitialize).EndInit()
In general, ushering in of any control leads to the generation of additional code in the above
format. An instance variable is created with the name of the class, followed by a number
beginning with 1, and not 0.

It is assumed that every control would be dealing with events. Therefore, it has BeginEvent and
EndEvent methods, which allow the control to be initialized without any interference. In
principle, they serve the purpose of a "Do Not Disturb" sign. The control is at liberty to act in
any way that it likes, until the EndEvent method is called. It even enjoys the license not to do
anything at all !

The abovementioned two methods are actually precautionary measures, which may or may not
be implemented.

The DataMember property is initialized to Null, the HeaderForeColor property is initialized to a


predefined color, the tab index is initialized to 0 and the name is initialized to DataGrid1. The
only important property is Location, since its co-ordinates determine the position on the Form.

You may wonder as to why Visual Studio.Net provides distinct properties for each control. The
logic behind this is that Visual Studio.Net is oblivious to what is being written, since it is the
job of the control to usher in the code that it yearns for. The AddRange function is used to add
the control to the Controls collection.

When the DataSource property of the DataGrid is set to DataSet, the line given below gets
inserted. It also affects the design time look of the DataGrid by displaying a plus sign.

Me.DataGrid1.DataSource = Me.DataSet11

Each time a property is altered, an additional line of code gets added to the Code Painter. Thus,
the DataSource property gets set to DataSet11.

Our view-point is that, every software developer should use Visual Studio.Net, since it is sure
to make each one of them more efficient programmers.

The different user interfaces of the Properties editor also ensure that, on most occasions, the
user does not have to enter a value. This is achieved by providing drop down listboxes
encompassing the various possible values, thereby eliminating the slightest probability of the
user committing a blunder.

Yet another advantage is that, very often, programming tends to become very tedious and
irksome. Therefore, it is a dream come true when the framework generates the code for us,
even if it does so only once in a while.

Next, the DataMember property is set to Customers. This does not add any fresh line of code,
but merely changes the previously written line to the following:
Me.DataGrid1.DataMember = "Customers"

We would definitely want you to attempt a small experiment. In the Design Mode, Set the
DataSource property to None. On doing so, the DataMember property too gets reset to None,
thus echoing the above action.

Visual Studio.Net transmutes itself into a feature of convenience, whereby, the dynamic
behaviour gets reflected by the code in the control itself, such as a DataGrid control. Next, we
insert the button control. This leads to the addition of the following code in the file:

Friend WithEvents Button1 As System.Windows.Forms.Button


Me.Button1 = New System.Windows.Forms.Button()
Me.Button1.Location = New System.Drawing.Point(96, 184)
Me.Button1.Name = "Button1"
Me.Button1.TabIndex = 1
Me.Button1.Text = "Button1"

As can be seen above, the outcome is very predictable when the code is generated by a
computer program. An instance variable Button1 is created, which can also handle events.

In the InitializeComponent function, the instance variable is actually instantiated, and the
properties of Location, Name, TabIndex and Text properties are set. The TabIndex property is
set to 1, since it is the second control that is being added.

Me.Controls.AddRange(New System.Windows.Forms.Control() {Me.Button1, Me.DataGrid1})

The utility of the AddRange function is demonstrated beyond doubt, when controls get added to
the Form. Note, that by using an array, the Button and the DataGrid are added concurrently.

When the Text property of the button is changed to 'Load', the following line gets added:

Me.Button1.Text = "Load"

We have decided to refrain from explaining the same code repetitively. The above can be
achieved by double clicking on the button and writing the code that is shown above. We are
nearing a state of considerable ease while working with Visual Studio.Net, since we are now in
a position to unravel as to what is happening internally.

The next application that we have chosen to build is that of a Master-Detail, or a Parent-Child,
or a One-To-Many relationship, using Visual Studio.Net. Firstly, we intend to display a list of
Customers in our data grid, and then, we wish to display the orders placed by each customer.

So, watch out! Here we go!

Click on the menu-option, File-New-Project. Now, you shall arrive at the New Project dialog box.
As usual, in the first pane, select the option of Visual Basic projects, and in the second pane,
select the option of Windows Applications. The name assigned to this project is t3, and the
project shall dwell in the directory v1. Then, click on the OK button.

The New Projects dialog box retains information about the projects that were last worked upon.
Hence, on many occasions, the previous values remain selected.

Thereafter, we select the DataAdapter control after clicking on the Data Tab in the ToolBox.
The wizard begins in the same way as before, i.e. by asking for information that it requires to
generate the code.

The screen 5.47 depicts the previous connection that we had made to the Northwind database.
Screen 5.47
And if it does not do so, click on the drop down listbox, which will display a list of connections
that have been created previously. So far, we have created only a single connection, thereby
rendering this list inconsequential as of now.

Ensure that the Northwind connection is selected, and then, click on the Next button. In the
screen for data retrieval, the default option of 'SQL statement' is left selected, since we are not
yet well versed in working with 'stored procedures'. Now, we click on the 'Next' button to arrive
at the screen where we have to enter the SQL statements. We could have used the Query
Builder again, but this time around, we would rather enter the following SQL statement
manually:

SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address, City FROM


Customers

Click on Finish to create the Connection and Adapter objects, which represent the connection
to the Customers table.

Now, we have to ferret data from the Orders tables. The same process is repeated, using which,
one more OleDbDataAdapter object is dragged onto the Form. The connection is once again
established with the Northwind database, as it contains both the tables of Customers and
Orders.

In the next screen, the data access method option of 'SQL Statement' is left selected. When we
click on the Next button, we are asked for the SQL statement. Here, we manually enter the
following SQL statement:

SELECT OrderID, CustomerID, EmployeeID, OrderDate, ShipCountry, ShipRegion, ShipCity FROM


Orders

To proceed further, click on the Finish button.

From the Orders table, we have randomly chosen some fields, keeping in mind that the
CustomerID field is of primary importance, since it is the only link between the two tables of
Customers and Orders. It is not mandatory for the Primary key and Foreign key to possess the
same name. However, both of them must be specified in the list of fields of the Select
statement. The outcome of this activity is the creation of a single control named
OleDbDataAdapter2. This is shown in screen 5.48.

Screen 5.48
The framework is extremely smart. It realizes that an attempt is being made to re-establish a
connection to the Northwind database. Therefore, it reuses the existing connection, instead of
creating a new one.

The Data is to be displayed in a control. So, at this juncture, instead of using the DataGrid, a
listbox from the Windows Form tab in the toolbox is used.

Bring in the listbox control, and then, click on Generate DataSet in the Data menu. The dialog
box that gets displayed, has a dataset name that we are quite content with, but the Customers
table is not shown as selected.
Screen 5.49
Select Customers, as shown in screen 5.49, and then, click on OK. This results in the addition
of one more control named DataSet11, to the list of three invisible controls.

Next, we click on View, followed by Solution Explorer, to activate the window. Then, double
click on the file named dataset1.xsd. This activates the XML Designer, as shown in screen
5.50.

Screen 5.50
Here, we come across two tables that constitute our dataset, viz. Customers and Orders. Each
table must necessarily have a Primary key, which is normally a single field. Thus, we see two
keys listed in front of the fields, which are the primary keys. The second column indicates the
data types of the fields.
The toolbox has only one tab named XML Schemas, from where the last control named
Relation is introduced into the Form. The control is dropped onto the Orders tables, since it is
the Child table. This brings up the Edit Relation dialog box.

Screen 5.51
The first textbox discloses the name of the relation i.e. CustomersOrders, which would be
displayed in the DataGrid. Then, there exist two listboxes, which when clicked upon, exhibit a
list of tables in the dataset of Customers and Orders, respectively.

The Parent table is the Customers table, since it contains unique values for the CustomerID,
and the child table is Orders, which has the Foreign key, encompassing multiple values for the
CustomerID. When we drop the relation object on the child, it picks up most of these values
and sets them as defaults. Hence, we do not have to enter them ourselves.

The dialog box is smart enough to select CustomerID as the Parent key, but it expects us to
select the Foreign key ourselves.

Therefore, we click on the down arrow, as in screen 5.52, and select the CustomerID column as
the Foreign key, and then, we click on OK.
Screen 5.52
The visual representation of this relationship is depicted in screen 5.53. The Customer table
has one unique id; and hence, it has a single arrow, whereas the Orders table has many
occurrences of the ids. Hence, CustomerID has multiple arrows leading to it.

Screen 5.53
Once the relation is set, click on the tab Form1.vb. Then, Drag and Drop a listbox control from
the ToolBox on the form, and move on to the property of DataSource, as shown in screen 5.54.
Set the value of the DataSource property to DataSet11.
Screen 5.54
The next property of DataMember shows a drop-down listbox, which when clicked on, displays
the two tables of Customers and Orders. This is shown in screen 5.55.

Screen 5.55
This occurs because the DataSource property is initialized to the dataset named DataSet11.
The two tables named Customers and Orders, which comprise the dataset object, are visible.
Screen 5.56
As we want to select the field of customer name, we click on the plus sign of the Customers
table. This leads to a display of all the fields specified in the Select statement.

The company name is a lot more intuitive than the customer id. So, the value of the property is
initialized to Customers.CompanyName, i.e. the name of the table, followed by a dot, followed
by the name of the field.

In order to authenticate the list, press the F5 key. This compiles and runs the project. The
output is extremely disappointing, since all that we see is an empty listbox, as shown in screen
5.57.

Screen 5.57
Close both, the Form window and the window labeled as Output. In the designer mode, drag
and drop a button from the ToolBox, and change its label to 'Load', as shown in screen 5.58.

Screen 5.58
Double click on the button, and enter the following code in the event handling function:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button1.Click
OleDbDataAdapter1.Fill(DataSet11)
End Sub

Here, as before, we call the Fill function of the OleDbDataAdapter object from the Customers
table, since presently, we are not interested in the data contained in the Orders table.

Press F5 to run the application, and then, click on the button labeled 'Load'. This results in the
listbox getting filled with data, as shown in the screen 5.59.

Screen 5.59
The Fill function is responsible for filling up the empty dataset, which is eventually displayed
by the control. This is because, the property in the control is set to this dataset. At this point in
time, the data from the Customers table is on display, but there is no sign of data from the
Orders table.
Now, to retrieve data from the Orders table, close the windows that have popped up while
running the program, and drag and drop a DataGrid onto the Form. Change the layout of the
control, as shown in screen 5.60.

Screen 5.60
The DataSource property in the DataGrid is set to DataSet11. For the DataMember field, the
property is set to the name of the Relation object and the table name
Customers.CustomersOrders.

Screen 5.61
Moreover, one line of code is inserted in the event handling code of the button.

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button1.Click
OleDbDataAdapter1.Fill(DataSet11)
OleDbDataAdapter2.Fill(DataSet11)
End Sub

Now, run the above program by pressing the F5 key. When the Form loads on, click on Load.
The screen that appears, looks like the screen 5.62, wherein a list of customer names is
displayed in the listbox, followed by the list of orders in the Orders tables.

Screen 5.62
Now, each time that a new customer is selected, the list of orders of that customer is displayed
in the DataGrid. This results from the two changes that we have effected: Firstly, we had filled
up the Orders table using the Fill function. Secondly, we had used the name of the relation as
a DataMember name.

The DataGrid, on learning that the name specified is not a table name, uses the relation object
to determine the records that need to be displayed.

Also, while clicking on a new customer name in the listbox, the DataGrid is apprised about the
new Customer that has been selected. Hence, it alters the records displayed in the DataGrid,
depending upon the CustomerID in the Orders table.

Me.DataGrid1.DataMember = "customers.customersorders"

When the DataMember in the datagrid is set to the relation object, the control field merely adds
one line, to initialize the property to the value. No other code gets inserted. Furthermore, no
DataRelation object gets created in the Form code. It is found in the file DataSet1.vb.

The above example appears to be too simplistic. So let us spice things up by adding a little
more complexity. The Orders table summarizes the orders as per the customer id. Moreover,
there exists a table named Order Details, which consists of the list of orders per order id.

What we wish to accomplish at this juncture is, to prepare a list of orders as per the order id,
when a certain order is clicked on. So, to begin with, we need an SQL statement to fetch
records from the Order Details table.
So, from the Data category in the toolbox, the control named OleDbDataAdapter is again
inducted into the Form. Then, we click on the Next button thrice:
• Firstly, on the opening screen.
• Secondly, on the Connection screen, since the Northwind connection is chosen by
default.
• Thirdly, on the screen in which the default of SQL Statement has been chosen.

In the text block, we now have to write the SQL Select statement, which fetches the records for
the specified fields from the Order Details table. A point to be noted is that, whenever the name
of a table contains a space, the table name must be placed within square brackets.

SELECT OrderID, ProductID, UnitPrice, Quantity, Discount FROM [Order Details]

When we click on Finish, one more OleDbDataAdapter object gets added at the bottom of the
Form.
The DataSet must be regenerated, for which the Generate option is selected from the Data
menu option.

The dialog box that appears contains only the newly added table as selected. But, we need to
select all the three tables, as seen in screen 5.63.

Screen 5.63
Finally, click on OK. In case a message box pops up, click on 'Yes to All'. Now, make sure that
the Solution explorer is visible.

If it is not, then click on the menu View-Solution Explorer, and then, double click on the
DataSet1.xsd file. As we invariably fall short of screen space, we recommend that you scroll to
the right to arrive at screen 5.64.
Screen 5.64
Here, we come across the third table named Order Details. After the table comes into sight,
drop the Relation object onto the Order Details table. Remember that a single order id in the
Orders table will have multiple records of the order id in the Order Details table.

In the Edit Relation dialog box, the name of the relation is shown as
CustomersOrder_x0020_Details. We will not amend it, since the framework automatically
changes it when the names of the Parent table and the Child table change. If you change the
name of the parent table from Customers to Orders, the name of the relation will change to
OrdersOrder_x0020_Details. If this does not happen, we request you to amend it manually to
OrderssOrder_x0020_Details.

For each order record found in the Orders table, there exists a list of order details in the Order
Details table. Therefore, the child element is assigned to Order Details. However, when you
click on the down arrow of the dropdown listbox, three tables will be displayed. This is because
the DataSet now comprises of three tables. Ensure that the child element contains the table
Order Details. In the Fields block, the Parent key field is OrderID. Therefore, the child key field
that is currently empty, is also changed to OrderID.

Make sure that your screen appears similar to what is seen in screen 5.65, and then, click on
OK.
Screen 5.65
The screen 5.66, which comes up next, displays a 'one to many relation' between the tables
Orders and Order Details. It is always advisable to periodically refresh everything, as well as to
save all the work. So, click on the menu option File-Save All.

Screen 5.66
In order to verify the fact that the relation object has been inserted, click on the property called
DataMember. The drop down listbox displays a plus sign next to the Orders.

Click on the plus sign to see the relation object as shown in screen 5.67.Thus, the DataMember
exhibits a total of two relations.
Screen 5.67
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles Button1.Click
OleDbDataAdapter1.Fill(DataSet11)
OleDbDataAdapter2.Fill(DataSet11)
OleDbDataAdapter3.Fill(DataSet11)
End Sub

Finally, before the project is executed, the Fill function of the OleDbDataAdapter3 is placed in
the Load button.

Run the application and click on the button. The screen that comes up is shown in screen
5.68. Here, we witness a list of Orders in the DataGrid, with each of them having a plus sign
placed next to it.
Screen 5.68
If you click on the plus sign, the screen 5.69 containing the name of the relation is seen. Now,
click on the relation name. This brings up the screen shown in screen 5.70, where the order
details for a specific order id are displayed.

Screen 5.69

Screen 5.70
Now, we present one more database application. In this example, we wish to display one record
at a time.

Click on the menu File-New-Project, and in the New Project dialog box, select the option of
Visual Basic projects in the first pane, and the option of Windows Application in the second
pane. Name the project as t4, and then, click on OK.

Usher in the OleDbDataAdapter object from the Data tab in the toolbox. This object is brought
in to enable us to insert an SQL select statement, which would retrieve data from the database.
Click on the Next button on the first three screens, and in the SQL statement textbox, enter the
following Select statement:

SELECT CompanyName, ContactName, ContactTitle FROM Customers

Since we are running short of time and space, we shall display only three fields from the
Customers table. On clicking the Finish button, the two objects of Adapter and Connection
object get created. Then, we generate the DataSet object, by selecting the menu option of
Generate DataSet from the Data menu. All the default settings are acceptable to us. Therefore,
we click on OK, resulting in the creation of a DataSet called DataSet1.

In this application, we want to display the data that has been retrieved from the database in
textboxes. So, three textboxes from the toolbox are dragged onto the Form. This is shown in
screen 5.71. These three textboxes are named as TextBox1, TextBox2 and TextBox3.
Screen 5.71
Now, make sure that the first textbox is selected. Scroll down the Properties window, until the
Data section property come into sight.

Then, click on the plus sign of DataBindings, as shown in screen 5.72. The main purpose of
this action is to set the Text property to a field in the database. The Text property of the textbox
has a drop-down listbox, which when clicked on, shows DataSet11, which is the lone dataset
created in the project.

Screen 5.72
A point to be considered here is that, we have not added a DataSet to our textbox. In spite of
this, the TextBox is astute enough to display all the DataSet objects that have been created in
the project.
Clicking on the plus sign of the DataSet object, will bring up a list of tables that comprise the
DataSet. Our listing reveals only one table named Customers. Then, we click on the plus sign
in front of Customers, to arrive at a list of fields that make up the table, as shown in screen
5.73.

Screen 5.73
Not all the fields from the table are visible, because the SQL Select statement shows the three
selected fields along with the Primary key.

Screen 5.74
Select the field of Company name. Doing so will change the value of the Text Property to
DataSet11 - Customers.CompanyName. The syntax for the value is as follows: The name of the
DataSet, followed by table name, and finally, followed by the field name. Thus, a field from the
table present in a DataSet is now bound to a textbox.

A similar approach is followed for the second textbox named TextBox2.

Here, we click on the textbox TextBox2 and scroll down to DataBindings. From the Drop down
list, which is obtained after clicking on the plus sign of DataSet11 and the Customers table, we
select the third field named ContactTitle.

For the third textbox, the above procedure is repeated, and the field named CustomerID is
selected.

Press F5 to run the program. The result of our actions is shown in screen 5.75, where some
default text has already been entered in the textboxes.

Screen 5.75
Since this is evidently distinct from what we had anticipated, we close the running application
and effect the requisite amendments.

Select the first TextBox and scroll down to the Text property. Then, delete the value assigned to
the Text property, as shown in screen 5.76.
Screen 5.76
Now, press Enter, and as an outcome of this, the textbox in the Form does not show any text at
all. The same process is reiterated for the other two textboxes.

If you have been sufficiently attentive and assiduous, you would have realized that earlier, the
Text property did not contain a database icon, but the moment the Data Binding Text property
is set to a field, the Text property obtains an icon, indicating the fact that the data property will
overwrite this Text property.

The designers of the TextBox control should have added one more feature in that, they should
have ensured that the Text property blanks out as soon as the field is set to a Data Text
property. The main reason that the textboxes do not display any data from the table is that, the
Customers table in the DataSet11 is empty. The Fill function has not been introduced so far,
and hence, it has not been executed.

Now, we introduce a Button by selecting it in the toolbox, and changing the Text to 'Fetch'.
Then, after double clicking on the button, the following code is introduced in the mouse event
handler, using the code painter:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button1.Click
OleDbDataAdapter1.Fill(DataSet11)
End Sub

Now, when the above program is executed by pressing the F5 key, the screen does not display
any text in the textboxes.

When the button is clicked, the window shows the first record, as seen in screen 5.77.
Screen 5.77
It gladdens the heart to see the record values in the textboxes. However, this is not enough,
since we intend to view the subsequent records also. So, close the windows of the running
application and add one more button, which shall facilitate scrolling to the succeeding records.
Change its label to 'Next', as shown in screen 5.78.

Screen 5.78
Double click on this button to add the following code to the event-handling sub of
Button2_Click.

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button2.Click
Dim b As BindingContext
b = BindingContext
Dim bb As BindingManagerBase
bb = b(DataSet11, "customers")
bb.Position += 1
End Sub

The class Form has a property called BindingContext, which returns a BindingContext object.
This object is stored in b. The task of a BindingContext object is to attend to all the
BindingManagerBase objects. So, by using b, which is a BindingContext containing the default
property or indexer of the DataSet, and using the table, the BindingManagerBase object can
easily be retrieved.

Every DataSet and table in the DataSet combination has its own BindingManagerBase object.
This BindingManagerBase object takes care of all the Binding objects. The
BindingManagerBase object has a property called Position, which is of type read-write. It
reveals the current active record in the Data Table. A new value can be assigned to it, in order
to make the record active.

Now, Run the project by pressing the F5 key. Then, click on the Fetch button. The first record
will get displayed. On clicking the Next button, the second record will get displayed, as shown
in screen 5.79. Thus, it is the Position property that establishes the active record.

Screen 5.79
Next, we intend to move backwards amidst the records. So, we repeat the same procedure,
wherein a button is brought into the Form from the toolbox and its label is changed to 'Prev'.
Then, the following lines of code are entered in the event handler function:

Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button3.Click
Dim b As BindingContext
b = BindingContext
Dim bb As BindingManagerBase
bb = b(DataSet11, "customers")
bb.Position = bb.Position - 1
End Sub

After pressing the F5 key, we first click on Fetch, followed by the Next button, which takes us
to the next record. Then, we click on the Prev button, and we are transported back to the
previous record. This is shown in screen 5.80. Thus, we have been able to scroll through a
series of records.
Screen 5.80
We would now like to implement the following:
• Move to the first or last record, depending on the button that is clicked.
• Display the current record number and the total number of records in the table.

To achieve this, we do the following: One more button is introduced in the Form, and its label
is changed to 'First'. This button is internally assigned the name of 'button4', and the following
code is inserted in it:

Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button4.Click
Dim b As BindingContext
b = BindingContext
Dim bb As BindingManagerBase
bb = b(DataSet11, "customers")
bb.Position = 0
End Sub

The only statement that has been modified in the code above is that of the Position property,
Which is set to 0, and not to 1, since the counting starts from 0.

Press F5 to run the application. First, click on the 'Fetch' button. Then, click on the 'Next'
button twice, to move to the third record. Now, click on the button labeled 'First'. This brings
us back to the first record.

Close the Form and revert back to Visual Studio.Net. Now, we shall display the current active
record number and the total number of records present in the customer table.

Select the TextBox control and drag and drop it onto the Form. This TextBox is named
textBox4, as shown in screen 5.81.
Screen 5.81
Delete the default value assigned to the Text property of the TextBox. Then, select the tab
Form1.vb, and at the very bottom, just prior to 'end class', enter the following lines of code in
Sub abc. The moment we press the Enter key for the Sub abc, the words End Sub
automatically get added. This is evident from screen 5.82.

Screen 5.82
Sub abc()
Dim c As Integer
Dim p As Integer
c = BindingContext(DataSet11, "customers").Count
p = BindingContext(DataSet11, "Customers").Position + 1
TextBox4.Text = "Record No " & p.ToString & " of " & c.ToString
End Sub
We start by creating two variables named c and p, both of type integer. The variable c will store
the number of records in the table, and p will store the current record position.

The BindingManagerBase object possesses a Position property for the current record number,
and a Count property for the total number of records that the table contains. The variables c
and p are initialized to the values returned by these properties, respectively.

An alternative approach would be to split up the above statement.

Firstly, the BindingContext property, which returns a BindingContext object, can be stored in a
variable. Then, an indexer that takes two strings, can be used to return a BindingManagerBase
object. Finally, the Count property of this object is furnished to determine the total number of
records in the table. We leave it to you to determine as to which of these two approaches is
simpler to comprehend.

We finally initialize the Text property to the string that results from displaying the variables c
and p. To write it all on a single line, the & sign is used, which acts like a delimiter while
concatenating strings. Before we can see the output, there is one more task to be performed.
The last objective is to introduce a button and label it as 'Last'. When this button is clicked on,
the last record should become visible. So, double click on it and enter the following code:

Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button5.Click
BindingContext(DataSet11, "Customers").Position = 10000
abc()
End Sub

In this sub, the Position property is assigned a value of 10000. We allocated such a mammoth
value, despite being cognizant of the fact that such a large number of records do not exist. This
is because, whenever a very large number is specified, which goes beyond the record count, the
active record becomes the last record.

The sub abc must be called in each of the four button events, in the manner shown above,
otherwise, the textbox will not be updated.

Another course of action to achieve the above would be to use the Count property, to ascertain
the total number of records, and then, to initialize the Position property to this value. In effect,
the accurate value is the count value minus 1, because the value of record position for the first
record is 0.

Now, when we click on Fetch, followed by the button labeled as Last, the last record becomes
visible, as shown in screen 5.83.
Screen 5.83
The textbox gets updated, informing us about the record that is currently active. Again, since
the Position property starts from 0 and not from 1, the value to be displayed first needs to be
incremented by 1.

This is the last application in the chapter, before we call it a day. What we intend to achieve
through this application is to display those records of customers in the datagrid, which meet a
certain criteria.

Create a new project by choosing the menu option File-New-Project. Then, in the New Project
dialog box, as usual, select Visual Basic projects in the first pane and Windows Application in
the second pane. The name assigned to this project is t5.

Since the application revolves around fetching data from the Customers table in the database,
the OleDbDataAdapter control is brought in from the toolbox. By this time, you must have
committed all the steps to memory, since this process has been harped upon throughout this
chapter. Everything remains the same, as has been explained earlier, except for the SQL Select
statement, which is as follows:

SELECT CustomerID, CompanyName, ContactName, ContactTitle FROM Customers where


(Country = ?)

The above SQL Select statement will retrieve records from the Customers table, where the field
Country matches a specified value. At this instant, the value is not known, hence the ? symbol
is used. The control ignores the new amendment in the syntax, and in its usual manner, it
generates the OleDbDataAdapter object and the OleDbConnection Object.

Now, click on the menu Data, followed by the Generate DataSet. All the values are acceptable
to us. Therefore, we leave everything untouched, and click on OK.

Subsequent to this, initiate a DataGrid control from the Windows Forms category in the
ToolBox into the Form. The DataSource property of this Form is set to DataSet11 and the
DataMember is set to Customers.
Then, bring in a button on the Form, and change its label to 'Fetch'. Then usher in a textbox
named TextBox1. Blank out the value assigned to the text property. The Form should look like
what is shown in screen 5.84.

Screen 5.84
Double click on the button, and enter the following code:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button1.Click
Dim c As System.Data.OleDb.OleDbCommand
c = OleDbDataAdapter1.SelectCommand
Dim p As System.Data.OleDb.OleDbParameterCollection
p = c.Parameters
Dim o As System.Data.OleDb.OleDbParameter
o = p("Country")
o.Value = TextBox1.Text
DataSet11.Clear()
OleDbDataAdapter1.Fill(DataSet11) End Sub

In the above code, the property SelectCommand of the OleDbDataAdapter1 object is used to
retrieve an OleDbCommand object.

This OleDbCommand object, which is present in the System.Data.OleDb namespace,


represents the Select statement entered in the OleDbDataAdapter wizard. It has a property
called 'parameters', which stores all the parameters in the SQL Select statement.

A parameter is a field in the 'where' clause, which is assigned a value of a ?. The parameters
property returns an OleDbParameterCollection object. Then, using an indexer, the individual
parameters can then be accessed. Since it is a collection object, multiple parameters can be
added to it.

The indexer takes the name of the field as a string parameter, in order to access the
parameters. It returns an OleDbParameter object, which has a member of Value. This member
is set to a value that relates to the field Country. This is what the user enters in the Textbox.
Thus, the value of the field country can be set to whatever the user enters in the textbox.

At times, the DataGrid may contain certain values from a previous query. Hence, it is a good
idea to Clear the contents of the table Customers. If the Clear function is not used, at times,
the residual data from some earlier query may linger on, thereby cluttering the existing data.

As usual, the Fill function fills up the dataset with records that match the value of the Country
that the user enters in the textbox.

Press F5 to run the program, and enter the name of the country as "UK". When the Fetch
button is clicked, the output displayed will be the records of customers who reside in UK,
thereby filtering the required records from amongst all the records in the table. This is shown
in screen 5.85.

Screen 5.85
This is an example of a dynamic query, wherein the user decides the criteria, based on which,
the data is dynamically retrieved and displayed.

 
6. XML and XSD
Let us now focus on XML, which seems to be the order of the day, since every developer worth
his salt seems to be singing this new mantra! Though it is impossible to comprehend XML in a
day, but we still are determined to give it a try.

We start by creating a new project called t6 in the c:\v1 subdirectory. For this, click on the
File-New-Project menu option. Then, select Visual Basic Project in the first pane, and select
Windows Application in the second pane. Click on OK to generate the smallest possible
Windows application.

Now, to add a new file to this project, click on File-New, and select the option of File instead of
Project, as shown in screen 6.1.

Screen 6.1
The dialog box that emerges is shown in screen 6.2. It displays a long list of file types that can
possibly be added to the project.
Screen 6.2
Normally, most files that are vital for the project get added automatically. However, we are still
rendered the flexibility of adding more files, if we so desire. Choose the option of XML file and
click on Open. This brings up an editor, with a single line written in it, as revealed in screen
6.3.

Screen 6.3
<?xml version="1.0" encoding="utf-8" ?>

The above stipulated line has to be the very first line of every XML document. XML stands for
eXtensible Markup Language. It is not a programming language like Visual Basic.Net, but a
language akin to English.
Had we created the file outside the Visual Studio.Net framework, the above line would have had
to be inserted manually. This is all.

In future, you hold the prerogative of creating the above file in any word processor that you
may fancy, since it is absurd to use Visual Studio.Net merely for editing purposes.

Every XML document adheres to certain rules. The first rule is that the first line in the
document must begin with <?xml and end with ?>. Any line beginning with <? is known as a
Directive. Rules are meant to be followed, with no questions asked. The second rule is that the
XML directive must have an attribute called version, with a value of 1.0.

An attribute is merely a word that is equated to some value. The value of the attribute version
can only be 1.0, since this is the only valid version of XML in existence. The second attribute to
the XML directive is 'encoding'. This attribute is optional. The value of utf-8 assigned to it
signifies that the words that appear in the file henceforth, belong to the English language, and
not to any other language, such as Chinese, etc.

We now append some more lines as follows:

On the next line, enter the word <customerslist>. Immediately after the 'greater than' sign, the
same word is entered again within angle brackets, but this time with the / sign preceding it.
This is represented in screen 6.4, where we see the following text:

<customerslist></customerslist>

Screen 6.4
The code editor can surely read our minds! The rationale behind insertion of the above text by
the code editor is that, any word that begins with an angle bracket, must also end with an
angle bracket.

The words enclosed within angle brackets are called 'tags'. Thus, customerslist is a tag. Every
start tag must have an end tag. An end tag is similar to the start tag, except that it has the /
sign placed before the tag name. The editor is smart enough to generate the end tag for us.
The first tag that is encountered in the XML file is called the root tag. All other tags must be
placed within this tag. The root tag is mandatory. It can be assigned any name, since there are
no rules in XML governing this aspect.

We have created the XML file to store a list of two customers. The basic information supplied
for each customer is the name and the phone number. You have to enter the additional lines,
so that the XML file finally represents the text given below in the specified format.

<?xml version="1.0" encoding="utf-8" ?>


<customerslist>
<cust>
<name>Vijay Mukhi</name>
<phone>4964339</phone>
</cust>
<cust>
<name>Sonal</name>
<phone>4964338</phone>
</cust>
</customerslist>

The details regarding each customer are enclosed within the tags of cust and /cust. Moreover,
within the cust tags, there exist two child tags called name and phone, both of which follow the
same rules.

Remember that the end tags get inserted automatically in the XML Editor. To save this file,
click on 'File- Save XMLFIle1 As' menu option. This is shown in screen 6.5.

Screen 6.5
This brings us to a dialog box as shown in screen 6.6, where we change the sub directory to
point to the root directory, and name the XML file as yyy. This results in the above XML file
being saved as yyy.xml in the root directory.
Screen 6.6
An alternate approach to achieve the above would be to simply fire up a word processor, enter
the above lines manually, and then, save it in a file named yyy.xml.

Once the file has been created, bring in a DataGrid control, followed by a button. Then, double
click on the button and enter the following lines of code:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button1.Click
Dim d As DataSet
d = New DataSet("zzz")
d.ReadXml("c:/yyy.xml")
DataGrid1.DataSource = d
DataGrid1.DataMember = "cust"
End Sub

In the above code, a new DataSet object 'd' is created, with the constructor being assigned the
name zzz. Naming the DataSet as zzz is optional. The ReadXml function from the dataset class
is then employed, to populate the DataSet object.

This function is furnished with the full path of the above XML file. The end result is that the
function fills up the dataset with records from a table called Cust. This occurs because the
customer data is enclosed within the cust tag.

The DataSource property of the DataGrid requires a DataSet object. Therefore, it is initialized
to d, which is the freshly created dataset.

There are alternative approaches to performing any task. Similarly, a dataset can also be
populated by fetching data either from a database table, or from a file on disk. The
DataMember property is set to the cust table in the DataSet.

Press F5 to run the program. Doing so will result in the display of a button labeled Button1
and an empty DataGrid. Click on the button to populate the grid with the two records from the
XML file, as shown in screen 6.7.
Screen 6.7
The names of the tags become the field names. Therefore, two fields are visible, i.e. 'name' and
'phone'. Further, the details provided for each customer are represented as records in the table.

This example illustrates the fact that a dataset can be filled up with records from an XML file,
without letting the DataGrid control get a whiff of it. The Datagrid couldn't care less about the
origin of the data, as long as it receives a dataset.

The same example can also be re-written, using one of the features in Visual Basic.Net.

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button1.Click
Dim d As DataSet
d = New DataSet("zzz")
d.ReadXml("c:/yyy.xml")
With DataGrid1
.DataSource = d
.DataMember = "cust"
End With
End Sub

Modify the code contained in the button, to the code stipulated above. The output however
remains the same.

The 'With' statement calls for a name. Hence, we have provide the name of the DataGrid object
to it, i.e. DataGrid1. In addition to this, all lines of code upto the 'End With', must begin with a
dot. This approach precludes the need to use the object name, along with its properties, over
and over again. Hence, if you are handling multiple properties of the same object, enclose
them within 'With' and 'End With'.

The above method is often encountered in the samples provided with Visual Studio.Net. Hence,
we decided to implement it here. Finally, it is your prerogative to decide whether you wish to
write the control name every time, or you would rather write it once and place dots before every
property name thereafter.

Click on the tab yyy.xml, and then, add the following lines of XML to the file:

<?xml version="1.0" encoding="utf-8" ?>


<customerslist>
<cust>
<name>Vijay Mukhi</name>
<phone>4964339</phone>
</cust>
<product>
<productid>10</productid>
<productname>vijay</productname>
<price>100</price>
</product>
<cust>
<name>Sonal</name>
<phone>4964338</phone>
</cust>
</customerslist>

Then we click on the tab Form1.vb Design, and bring in a textbox called TextBox1. The text
property of the control is also blanked out. Then double click on the button and write out the
following code:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button1.Click
Dim d As DataSet
d = New DataSet("zzz")
d.ReadXml("c:/yyy.xml")
With DataGrid1
.DataSource = d
.DataMember = TextBox1.Text
End With
End Sub

Other than the modification in one of the lines, the rest of the code remains the same. The
DataMember property that was earlier set to cust, is now initialized to the Text property of the
TextBox.

Now, press F5 to run the program. In the textbox, enter the name of the table as Product.
Then, click on the button. The output is depicted in screen 6.8.
Screen 6.8
In the XML file, another tag called Product has been added. Thus, the DataSet now contains
two tables, i.e. cust and product. The user can either enter 'cust' or 'product' in the textbox.
Depending on the name assigned to the DataMember property of the DataGrid, data from the
selected table is displayed.

Close the running application. Then, click on the textbox to select it, and press the delete key.
To delete the DataGrid control, use the menu option, i.e. first select the Datagrid control, and
then, click on the menu option of Edit-Delete.

Now, bring in a new textbox control. The only problem with a textbox is that, even though you
can alter its size, by default it displays only a single line.

To incorporate multiple lines in the textbox, select the control and scroll down to the property
named MultiLine in the properties window. The default value assigned to this control is False.
Click on the drop down listbox, as shown in screen 6.9, and select the value of True.
Screen 6.9
Since this property now has a value of True, we are allowed to change the vertical height of the
textbox, as shown in screen 6.10.

Screen 6.10
Double click on the button, and replace the existing event handling code with the code
specified below.

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button1.Click
Dim s As New System.IO.StringWriter()
Dim d As DataSet
d = New DataSet("hi")
d.WriteXmlSchema(s)
TextBox1.Text = s.ToString
End Sub

Now, press F5 to run the program. When you click on the button, the textbox displays some
text. This is seen in screen 6.11.

Screen 6.11
The text entered in the textbox is shown below.

<?xml version="1.0" encoding="utf-16"?>


<xs:schema id="hi" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="hi" msdata:IsDataSet="true">
<xs:complexType>
<xs:choice maxOccurs="unbounded" />
</xs:complexType>
</xs:element>
</xs:schema>

In the click event of the button, the Dim statement is used to define and create an object 's' as
an instance of the StringWriter class, from the System.IO namespace. The StringWriter class
deals with large strings very effectively.

We then create a new DataSet object 'd', and call the function WriteXmlSchema from it. This
function accepts a StringWriter parameter 's' and writes the XML schema to it. The Text
property of the TextBox is then initialized to the ToString function from the TextWriter class.

In the .Net world, all classes normally have a ToString function, which returns the string
representation of any object. In this case, the WriteXmlSchema function writes out the entire
schema, which is then displayed in a String format.

Let us now attempt to understand what an XML schema actually entails.


An XML schema file is an XML document, and hence, like all XML documents, it starts with
the XML directive statement. The directive statement is followed by the root tag named
'schema'.

An XML schema has to begin with a tag called 'schema'. However, all and sundry can create
tags such as schema. So, to avoid any mis-match, the tag is prefixed with a namespace
followed by a colon. The schema tag is prefixed with 'xs' and a colon. The name of the prefix is
not very significant. What is more important is the attribute xs, which uniquely identifies the
xs namespace, and thus, the schema tag. In this case, the xs namespace prefix points to the
URI http://www.w3.org/2001/XMLSchema.

This file contains the rules for the elements that belong to the xs namespace. We shall explain
each of these rules, one at a time. The xs namespace prefix belongs to the xmlns namespace
prefix, which is not set to anything. The attribute of 'id' merely identifies the schema. It is
assigned the name of the DataSet, i.e. "hi". This part can be safely ignored for the time being.

Yet another namespace prefix of 'msdata' is created. Thus, all tags prefaced with msdata belong
to a namespace that is distinct from the tags prefaced with 'xs'. This concept of namespaces is
identical to that of the namespaces in Visual Basic.Net. We utilize this concept to uniquely
identify an entity.

The schema element starts with the element tag, with the name attribute initialized to "hi". The
attribute IsDataSet is set to True, to confirm the representation of the DataSet.

The complexType tag is a child in the element tag. It defines the type for the element named
"hi". The complexType is followed by a choice element, which permits us to pick out our choice
from amongst the options that it contains. We shall not explain this any further at this
juncture, since there is nothing to choose from.

The maxOccurs attribute decides on the occurrence of the elements that follow. The value of
'Unbounded' indicated that there are no limits. The 'choice' tag is a single tag, as it ends with a
/. The above Schema is incomplete, since there is absolutely no data in the DataSet.

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)


Handles Button1.Click
Dim s As New System.IO.StringWriter()
Dim d As DataSet
d = New DataSet("hi")
d.ReadXml("C:\yyy.xml")
d.WriteXmlSchema(s)
TextBox1.Text = s.ToString
End Sub

This shortcoming is corrected in the code given above. The only change that has been
augmented is the addition of the ReadXml function. This populates the XML schema with data
from the file yyy.xml.

Before executing the above program, select the textbox control, and then, scroll down the
property window. Then, change the value for the ScrollBars property from None to Both. As an
outcome of this, if and when the data in the textbox exceeds the horizontal or vertical
boundaries, the system will automatically generate a scrollbar. Press F5 to run the program,
and then, click on the button. The screen 6.12 shows a vertical scrollbar.
Screen 6.12
The schema file that is listed in the textbox, is given below.

<?xml version="1.0" encoding="utf-16"?>


<xs:schema id="customerslist" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="customerslist" msdata:IsDataSet="true">
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<xs:element name="cust">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" minOccurs="0" />
<xs:element name="phone" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="product">
<xs:complexType>
<xs:sequence>
<xs:element name="productid" type="xs:string" minOccurs="0" />
<xs:element name="productname" type="xs:string" minOccurs="0" />
<xs:element name="price" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>

The Schema file incorporates many new lines. The element no longer uses the name "hi" as the
name of the dataset; instead, it takes the root tag of customerslist specified in the XML file. The
complexType contains data that describes itself better.
The 'choice' element contains two element tags, since the DataSet comprises of two tables of
'cust' and 'product'. The maxOccurs attribute specifies that the two tables can occur as many
times as is required. The 'cust' and 'product' tags should be permitted to occur infinite number
of times, as there is no restriction on the number of records that these tables may contain.

The next thing of importance is a description of the data structure of these tables, embodying
the field names, their data types, etc. This is what the XML schema world is all about. It is
used to describe entities, and to ensure that it replaces SQL while the data in the databases is
being defined.

There exists an element called 'cust', followed by the complexType element. A 'sequence
element' basically lists out the order that is to be followed. The 'cust' table has two fields, viz.
name and phone. Therefore, there are two elements representing the two fields.

The type attribute comprises of the data type of the fields, and the minOccurs has a value of 0.
This indicates the fact that, for the field, the minimum number of occurrences is 0, i.e. the field
is optional. The default value of maxOccurs is 1. Thus, the element need not be present, but if
it is present, it can only occur once. Thereafter, the sequence, the complexType and the
element tags are all closed. The same procedure is then repeated for the product table.

Although the XML schema gives a description of the data, it actually contains no data at all.
The newer versions of SQL Server have avoided using SQL. Instead, they store data in the XML
format, thus allowing any entity to access this data. The very rationale behind using schemas
is that, they are very expressive and they define rules, which the data in our XML files must
follow.

We commence by creating our own XML schema file, using the Schema Painter provided by
Visual Studio.Net.

As always, create a simple windows application by clicking on File-New-Project menu-option,


and by selecting Visual Basic projects and Windows application, in their respective panes.
Name the project as t7, and store it in the c:\v1 subdirectory.

From here onwards, we do things differently. We add a new item to our project, but instead of
choosing the New option, we opt for the 'Add New Item' from the File menu, as shown in screen
6.13.
Screen 6.13
This brings us to screen 6.14, where the dialog box contains a dozen things. The option that
fascinates us is the XML Schema item. Therefore, we click on it and change its name to
zzz.xsd.

Screen 6.14
Then, we click on Open. This brings up the file zzz.xsd in the Solution Explorer, and it also
kick starts the Schema Painter, as shown in screen 6.15.
Screen 6.15
The toolbox also undergoes transformation, displaying a large number of elements that can be
added to the painter. If we click on the XML option given at the bottom, it transports us to the
XML code that gets generated, whenever the schema is changed. The screen 6.16 shows the
option.

Screen 6.16
The XML schema file that gets created, is shown below:

<?xml version="1.0" encoding="utf-8" ?>


<xs:schema id="zzz" targetNamespace="http://tempuri.org/zzz.xsd"
elementFormDefault="qualified" xmlns="http://tempuri.org/zzz.xsd"
xmlns:mstns="http://tempuri.org/zzz.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema">
</xs:schema>
The file is very similar to the one we explained earlier. However, it now has some new attributes
added to it.

The attribute targetNamespace contains the location of the elements that do not have a
namespace prefix. The elementFormDefault attribute can take one of the two values of
'qualified' or 'unqualified'. The default value is 'unqualified'. Since the value chosen is
'qualified', all elements from the target namespaces that are referred to, have to be qualified
with the namespace prefix.

Had we clung on to the default value, there would have been no need for qualifying the
elements, provided that they belong to the target namespace. In addition to these, yet another
namespace prefix called 'mstns' is created.

We will now switch back to the Schema tab at the bottom, and click on the simpleType control.
A mere click on it does no wonders. The control must be dragged and dropped onto the form.

Change the name from simpleType1 to 'vijay', and leave the data type string unchanged. Screen
6.17 exhibits the outcome of our actions.

Screen 6.17
If you are also burning with curiosity as we were, you would click on the word 'string'. This will
display a drop down listbox as seen in screen 6.18.
Screen 6.18
Now, switch over to the XML tab to view some fresh XML code that has been incorporated.

<?xml version="1.0" encoding="utf-8" ?>


<xs:schema id="zzz" targetName_pace="http://tempuri.org/zzz.xsd"
elementFormDefault="qualified" xmlns="http://tempuri.org/zzz.xsd"
xmlns:mstns="http://tempuri.org/zzz.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="vijay">
<xs:restriction base="xs:string" />
</xs:simpleType>
</xs:schema>

Henceforth, only the newly generated sections of the file would be shown, since we see no point
in repeating the explanation of the schema element, over and over again.

An element is the basic entity for a schema, since it represents a field or a DataSet. Every
element needs a type. There are basically two types in the schema world, viz. complexType and
simpleType. To represent simple data, a simpleType is used, and to represent complex data, a
complexType is used. The data types of string and integer are built-in types, which the schema
world understands. Hence, for these types, the simple type is used. However, in the case of
user-defined data types, normally the complexType is employed.

'Vijay' is a simpleType. It determines the actual data type of the data that the entity can
represent. Now, since we have specified 'string', the framework ensures that any element using
the type 'vijay', would only have string values. So, 'vijay' and 'string' do the same job, and can
also be used interchangeably.

Furthermore, the 'restriction' element can be used to specify restrictions on the base type. So,
let us now work towards restricting the types of strings, which can be used with the type 'vijay'.
Click on the row below the St word. This would display the varied options that can be used
here. We see only the option of Facet, as seen in screen 6.19.
Screen 6.19
A Facet is the only option that can be used with simpleType. There are many more options that
can be employed with the complexType.

We then click on the listbox on the right, and arrive at screen 6.20, which displays the options
that are available.

Screen 6.20
Select the option of 'pattern' and enter '\D{3}' in the textbox on the right, as seen in screen
6.21.
Screen 6.21
Click on the XML tab to see the XML code generated for this action.

<xs:simpleType name="vijay">
<xs:restriction base="xs:string">
<xs:pattUrn value="\D{3}" />
</xs:restriction>
</xs:simpleType>

We could have written the above XML ourselves, but the Schema Designer makes our life much
simpler. The 'pattern' option gets added as pattUrn, and the value of \D{3} that is entered,
becomes the value of the attribute value. A pattern is a synonym for a regular expression.

For instance, when the user keys-in a value, the correct characters for the type specified must
be entered for the element. For example, in an e-mail address, the @ sign must be inserted at
the correct place. People have written volumes on 'regular expressions', which is worth a
perusal in your spare time.

The character \D stands for any Non-Digit characters, and \d stands for a digit from 0 to 9.
The curly braces expect a number, so we have specified 3 as the number, since we want 3 non-
digit characters. Thus, the type 'vijay' is a string, which can have any three non-digit
characters. We may have as many facets as we desire.
Screen 6.22
In screen 6.22, we have three facets, each of which is an enumeration, with the values of abc,
pqr and xyz, respectively. Let us now examine the XML code that is generated.

<xs:simpleType name="vijay">
<xs:restriction base="xs:string">
<xs:enumeration value="abc" />
<xs:enumeration value="pqr" />
<xs:enumeration value="xyz" />
</xs:restriction>
</xs:simpleType>

The enumeration facet empowers us to choose a single value from amongst numerous values.
In the above case, the type 'vijay' has three enumerated values of abc, pqr and xyz. In this
manner, we can restrict the values that the simpleType can represent.

Let us now add a complexType to our designer. Click on the schema tab on the bottom to
switch to the Schema Designer, and then, drag and drop the complexType onto it, from the
toolbox. The name of the complexType is changed to Mukhi. You can click on the column next
to Mukhi, to view the options that are provided. We prefer to leave it blank. Then, click on the
row below Mukhi. This displays a series of elements, as seen in screen 6.23.
Screen 6.23
Here, we select sequence. Doing so automatically brings up a group box named group1, of type
'sequence'. The group must contain two fields. So, enter 'f1' in the row below group1, and then,
select 'string' in the column besides it. To add the second field, click on the next row and enter
the second field name as 'f2' with the type as 'string'. This brings us to screen 6.24.

Screen 6.24
The first column has a value of E, which represents an element.

<xs:complexType name="mukhi">
<xs:sequence>
<xs:sequence>
<xs:element name="f1" type="xs:string" />
<xs:element name="f2" type="xs:string" />
</xs:sequence>
</xs:sequence>
</xs:complexType>

The XML code very clearly specifies a complexType element named Mukhi, which has two
sequences. The second sequence has two fields, named f1 and f2.

The first sequence is not required. However, we have included it to merely display the visual
effect of the Schema Designer. The above XML is the same code that was revealed to you some
time back.

Now, from the toolbox, select element, and drag and drop it into the form. Change the name of
the element to 'sonal'. For selecting the type, click on the right hand column. This will show the
list of types available, including the type Mukhi. This is shown in screen 6.25. Select the type
Mukhi and witness the changes in the Designer.

Screen 6.25
The screen 6.26 shows us the linkages visually.
Screen 6.26
Thus, the user-defined types, whether simple or complex, also get added to the list of types.
Now, drag-and-drop one more element from the toolbox in the XML Designer, and name it as
'yyy', as shown in screen 6.27, leaving the type unchanged.

Screen 6.27
Drag the element sonal that was created above, onto this default element. Screen 6.28 shows
that the elements get bound to each other, beginning with yyy, followed by sonal and then,
followed by the sequence named Mukhi.
Screen 6.28
Let us now examine the entire XML file that has been written out by the Schema Designer.

<?xml version="1.0" encoding="utf-8" ?>


<xs:schema id="zzz" targetNamespace="http://tempuri.org/zzz.xsd"
elementFormDefault="qualified" xmlns="http://tempuri.org/zzz.xsd"
xmlns:mstns="http://tempuri.org/zzz.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="vijay">
<xs:restriction base="xs:string">
<xs:enumeration value="abc" />
<xs:enumeration value="pqr" />
<xs:enumeration value="xyz" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="mukhi">
<xs:sequence>
<xs:sequence>
<xs:element name="f1" type="xs:string" />
<xs:element name="f2" type="xs:string" />
</xs:sequence>
</xs:sequence>
</xs:complexType>
<xs:element name="yyy">
<xs:complexType>
<xs:sequence>
<xs:element name="sonal" type="mukhi">
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

The XSD file contains a simple type named vijay, followed by a complex type named Mukhi.
Then, there exists an element named yyy that starts with a complexType, followed by a
sequence containing one more element called sonal, of type Mukhi. The tags are duly closed.
Thus, you can see that the creation of schemas has been considerably simplified by the
Schema Designer. Before proceeding any further, click on File-Save All menu option.

Now, to create a new XML file, click on File-New-File, and then, in the New File dialog box,
choose XML file, as before. In the properties window, click on the listbox for the property
targetSchema, as shown in screen 6.29.

Screen 6.29
Select the schema as zzz.xsd or http://tempuri.org/zzz.xsd. The text in the XML file changes to
the following:

<?xml version="1.0" encoding="utf-8" ?>


<yyy xmlns="http://tempuri.org/zzz.xsd">
</yyy>

Since we named the element as yyy having no type, the root element becomes yyy. The default
namespace is determined by the xmlns attribute, which points to the zzz.xsd file that we had
created.

Then, we click in the XML editor and enter a < sign, as shown in screen 6.30. This displays the
element sonal. Here, we are permitted to enter only sonal, since it is the sole element in our
file.
Screen 6.30
This corroborates the fact that the XML file constantly looks up the XSD file to ensure that the
data that has been entered, complies with the rules of the Schema file.

Select sonal, and as before, the ending tag gets created for us. Make sure that the end tag is
placed on a new line. Insert one more blank line within the tags. Now, within the start and the
end tag, enter the < sign. As shown in screen 6.31, the two fields f1 and f2 are depicted,
because the element sonal is of the complexType mukhi, which contains only the two fields f1
and f2.

Screen 6.31
Select the field f1. The end tag immediately shows up. Enter any text such as "vij" in the tag f1,
and then enter the following lines:
<?xml version="1.0" encoding="utf-8" ?>
<yyy xmlns="http://tempuri.org/zzz.xsd">
<sonall>
<f1>vij</f1>
<ggg>jo</ggg>
</sonall>
</yyy>

The end result is exhibited in screen 6.32.

Screen 6.32
The tag ggg is not a part of the schema. Hence, it is underlined in red. Any tag that does not
meet the schema rules is rejected, and this violation is indicated by a change in the color.
Thus, the editor too points fingers at our mistakes.

By now, you must have realized that Visual Studio.Net makes life much simpler for all of us.
However, it cannot be used to comprehend the Visual Basic language. So, henceforth, upto the
end of this volume, we shall focus purely on programming concepts, without using the
framework.

 
7. Exception Handling
Henceforth, all the topics would be covered in considerable depth. In one of the previous
chapters, we enlightened you about the errors that occurred only at run time, which the
compiler could not detect. These errors are termed as 'exceptions'. The occurrence of an
exception signifies that something extra-ordinary has transpired, which need not necessarily be
an error.

For instance, in a subroutine, some of the potential errors that may occur are:
• The parameter supplied to it could be incorrect.
• There could be difficulty in opening a file on disk, etc.

Since a subroutine has no means of returning values to indicate success or failure, it becomes
difficult to identify the cause or the type of error that has occurred.

To be bailed out of a predicament like this, the concept of exceptions comes in handy. For every
distinct type of error that occurs, a unique exception can be thrown.

a.vb
public class zzz
shared sub Main()
dim a as System.Exception
a = new System.Exception("hi")
Throw a
end sub
end class

>vbc a.vb
>a

Error
Unhandled Exception: System.Exception: Hi
at zzz.Main()

The Throw statement is the only pathway to execute an exception. This keyword requires an
Exception object. Therefore, the object 'a' is declared as an instance of the Exception class,
where the parameter of "hi" is passed to the constructor. The Throw statement now 'throws' or
'raises' an exception, and displays the error message as "Hi".

On running the above program, a mammoth dialog box pops up. We request you to click on the
'No' button, or else you could be transported to the Debugger.

a.vb
public class zzz
shared sub Main()
abc
System.Console.WriteLine("After abc")
end sub
shared sub abc
dim a as System.Exception
a = new System.Exception("hi")
Throw a
System.Console.WriteLine("After throw")
end sub
end class

Error
Unhandled Exception: System.Exception: hi
at zzz.abc()
at zzz.Main()

The above example sheds some more light on the concept of Exception Handling. The main sub
has a shared sub named abc, whose sole task is to throw an exception.

The output displayed validates the fact that a part of the code can throw exceptions too. Since
the sub abc throws an exception, the output points to the sub. However, since the sub abc is
called from Main sub, Main also gets displayed.

Thus, whenever we come across multiple lines referring to an exception, we have to read them
in a specific sequence, i.e. front to back. The closest sub is the one that is responsible for
throwing the exception, and the other subs are the ones that contain the previous subs.

Here, a point to be borne in mind is that the program comes to a grinding halt when an
exception gets thrown. As a consequence of this, none of the code, either in the sub abc or in
the sub main, gets called thereafter, and the application comes to an end.

Under certain situations, this type of abrupt ending of the program is totally unacceptable,
since any sub called from any of the classes could throw an exception and bring the program to
a standstill.

The next program reveals as to how this can be prevented from occurring.

a.vb
public class zzz
shared sub Main()
try
System.Console.WriteLine("Before abc")
abc
System.Console.WriteLine("After abc")
catch
System.Console.WriteLine("In Catch")
end try
System.Console.WriteLine("After try")
end sub
shared sub abc
dim a as System.Exception
a = new System.Exception("hi")
Throw a
System.Console.WriteLine("After throw")
end sub
end class

Output
Before abc
In Catch
After try

The above program neither displays any exception dialog box, nor does it quit on encountering
the 'throw' statement. It completes execution upto the very end.

The first WriteLine function executes normally, followed by the call to the sub abc. Thereafter,
the sub throws an exception, as a result of which, all the lines of code after the 'throw' get
ignored.

The significant point here is that, the sub abc has been placed in a try-catch clause, which is
why the dialog box does not get displayed. Instead, the program executes code in the 'catch',
and then, moves out gracefully at the end try statement. No code gets called after the sub abc
has been called.

Thus, by placing all the exception handling code within the catch, all the probable exceptions
can be suitably catered for. The code that is to be placed in the catch block is left entirely to
your discretion.

Depending upon the situation, you may either place all the subs and functions in one gigantic
'try and catch' statement, or place them in individual 'try and catch' statements. Visual
Basic.Net has no rules in this regard.

a.vb
public class zzz
dim shared i as integer = 10
shared sub Main()
try
System.Console.WriteLine("Before abc")
abc
System.Console.WriteLine("After abc")
catch e as System.Exception when i <= 5
System.Console.WriteLine("In Catch")
end try
System.Console.WriteLine("After try")
end sub
shared sub abc
dim a as System.Exception
a = new System.Exception("hi")
Throw a
System.Console.WriteLine("After throw")
end sub
end class

Output
Before abc

Unhandled Exception: System.Exception: hi


at zzz.abc()
at zzz.Main()

When we run the above program, the offending dialog box gets displayed, as before. This
happens because, in addition to the Exception argument, the 'catch' clause also requires a
'when' clause along with a condition. We have created a variable i and set its value to 10.
While catching the exception, the condition is evaluated. Since the condition results in a value
of False, the catch is overlooked, thus leaving it to the system to handle the exception. It
behaves as though the catch statement never existed.

a.vb
public class zzz
dim shared i as integer = 10
shared sub Main()
try
System.Console.WriteLine("Before abc")
abc
System.Console.WriteLine("After abc")
catch e as System.Exception when i <= 5
System.Console.WriteLine("In Catch i <= 5")
catch e as System.Exception when i > 5
System.Console.WriteLine("In Catch i > 5")
System.Console.WriteLine(e.ToString)
end try
System.Console.WriteLine("After try")
end sub
shared sub abc
dim a as System.Exception
a = new System.Exception("hi")
Throw a
System.Console.WriteLine("After throw")
end sub
end class

Output
Before abc
In Catch i > 5
System.Exception: hi
at zzz.abc()
at zzz.Main()
After try

The above program does not display any dialog boxes, since it contains two catch statements:
• The first one handles exceptions when the value of the variable i is less than 5.
• The second one caters to the exceptions when the value of i is greater than 5.

Thus, depending on the value of the variable i, one of the two catch statements gets called.

If we change the condition of the first catch to 'when i > 5', even though both the conditions are
the same, Visual Basic.Net does not complain. This is because, the moment any one of the
conditions gets satisfied, all the other conditions get ignored.

The string representation of the exception object is displayed using the sub ToString, which is
similar to the manner in which error messages are displayed.

a.vb
public class zzz
dim shared i as integer = 10
shared sub Main()
try
System.Console.WriteLine("First try")
try
System.Console.WriteLine("Before abc")
abc
System.Console.WriteLine("After abc")
catch
System.Console.WriteLine("In second Catch")
end try
System.Console.WriteLine("After first try")
catch
System.Console.WriteLine("In First catch")
end try
System.Console.WriteLine("After second try")
end sub
shared sub abc
dim a as System.Exception
a = new System.Exception("hi")
Throw a
System.Console.WriteLine("After throw")
end sub
end class

Output
First try
Before abc
In second Catch
After first try
After second try
In the above program, there are two try statements, one ensconced within the other. In the first
try statement, the WriteLine function gets executes. In the second try statement, the WriteLine
function gets executes once again, without much ado!

The exception thrown by the sub abc is caught in the catch of the inner try statement. Once
the catch completes execution of its code, the program moves from the inner try to the outer
try. Since the exception has already been caught and handled by the inner try, the outer catch
does not catch it again.

Thus, as long as one of the catch statements is able to catch the exception that has been
thrown in the try, the other catch statements will behave as though no exception was thrown.
This would occur even if the condition of the second try statement is satisfied.

Thus, as long as even one catch statement in the sequence catches the exception, none of the
other catch statement gets called.

a.vb
public class zzz
shared sub Main()
try
abc
catch
System.Console.WriteLine("In Catch ")
finally
System.Console.WriteLine("In Finally")
end try
System.Console.WriteLine("After try")
end sub
shared sub abc
dim a as System.Exception
a = new System.Exception("hi")
Throw a
System.Console.WriteLine("After throw")
end sub
end class

Output
In Catch
In Finally
After try

The 'finally' clause is an integral part of the 'try-catch-finally' statements. The exception that is
thrown can be easily caught in one of the catch statements. However, when there are multiple
catch statements, only one of them will get executed.

One problem that could befall us is that, there could be some common code that needs to be
executed for all the catch statements. One solution is to repeat the same code in each 'catch'
statement. The other option would be to use the 'finally' clause. After all the statements in the
'catch' block have been executed, just before the 'try' clause ends, the code placed in the
'finally' block gets called.

Now, comment out the line of code that calls the sub abc, thereby, evading all exceptions that
get thrown. Thus, when the program is executed, no exceptions are thrown. Nevertheless, the
code written in the 'finally' clause gets called.

a.vb
public class zzz
dim shared i as integer = 10
shared sub Main()
try
System.Console.WriteLine("First try")
try
System.Console.WriteLine("Before abc")
abc
System.Console.WriteLine("After abc")
catch when i >= 100
System.Console.WriteLine("In second Catch")
finally
System.Console.WriteLine("In second finally")
end try
System.Console.WriteLine("After first try")
catch
System.Console.WriteLine("In First catch")
finally
System.Console.WriteLine("In first finally")
end try
System.Console.WriteLine("After second try")
end sub
shared sub abc
dim a as System.Exception
a = new System.Exception("hi")
Throw a
System.Console.WriteLine("After throw")
end sub
end class

Output
First try
Before abc
In second finally
In First catch
In first finally
After second try

This program initiates two new concepts. As before, there are two 'try' statements. The sub abc
throws an exception, which the catch is unable to catch since the variable i has a value less
than 100, thereby causing the condition to evaluate to false. Now, the onus falls on the outer
try to catch the exception.

However, before exiting the inner try, it first calls the 'finally' clause of the inner 'try-catch-
finally' block, and then, it executes code in the outer 'catch' statement. Thereafter, it executes
the 'finally' block of the outer 'catch' statement. In case, the outer 'catch' fails to catch the
exception, the system will display the appalling dialog box, which has been shown earlier.

a.vb
public class zzz
shared sub Main()
try
abc
catch
System.Console.WriteLine("In Catch Exception")
end try
end sub
shared sub pqr
dim a as System.Exception
a = new System.Exception()
Throw a
end sub
shared sub abc
pqr
end sub
end class

Output
In Catch Exception

This example is a variation of the above example. It proves that when the exception has to be
caught, it moves up the sub hierarchy. Thus, the concept is that someone somewhere should
be able to catch the exception.

The Main subroutine calls abc, which in turn calls pqr. The sub pqr throws the exception.
Since there is no catch block in pqr, the catch within the sub abc should normally get called.
However, there is no catch present in the abc block, due to which, the catch in the main
function is evoked. The catch in the Main block displays 'In Catch Exception'.
However, if the sub Main too does not provide for the catch clause, the exception based dialog
box will get displayed. Thus, the presence of at least one try catch in the Main subroutine is
strongly prescribed.

a.vb
public class zzz
shared sub Main()
try
abc
catch
System.Console.WriteLine("In Catch ")
exit try
System.Console.WriteLine("In Catch 1")
finally
System.Console.WriteLine("In Finally")
end try
System.Console.WriteLine("After try")
end sub
shared sub abc
dim a as System.Exception
a = new System.Exception("hi")
Throw a
System.Console.WriteLine("After throw")
end sub
end class

Output
In Catch
In Finally
After try

The program introduces the statement of 'Exit Try', which ceases to process all the 'catch'
statements, and exits from the 'try' gracefully. Thus, the second occurrence of the WriteLine
function does not get executed.

The job of Exit Try is to abandon the try statement instantly. However, the program is not
permitted to sneak out of the try statement, without first paying homage to the finally clause.
The output reveals that the code in the finally clause has been executed before the 'try' ended.

a.vb
public class zzz
shared sub Main()
try
abc
catch e as System.Exception
System.Console.WriteLine("In Catch ")
end try
end sub
shared sub abc
dim a as xxx
a = new xxx()
Throw a
end sub
end class
class xxx
Inherits System.Exception
end class

Output
In Catch

An exception is simply any class that is derived from the exception class. The class xxx is an
exception handling class, since it derives from the class exception. In the sub abc, an exception
of type xxx is thrown. Note that it is not of type Exception.

However in the catch, the exception that is caught is of type Exception, and not of type xxx.
This is absolutely permissible, since all exceptions get caught by the type Exception, since they
derive from it.

a.vb
public class zzz
shared sub Main()
try
abc
catch e as xxx
System.Console.WriteLine("In Catch xxx")
catch e as System.Exception
System.Console.WriteLine("In Catch Exception")
end try
end sub
shared sub abc
dim a as xxx
a = new xxx()
Throw a
end sub
end class
class xxx
Inherits System.Exception
end class

Output
In Catch xxx

The appropriate approach to catch exceptions would be to check the exceptions that are
thrown by a sub. The above program has only one sub abc, which throws only one exception of
type xxx. However, if ten different types of exceptions are thrown, then good programming
practice dictates that each of the ten exceptions should be caught using different catch
statements.

Every catch should handle its exception individually. The exception that is not handled
separately can be caught by the type Exception. Now, merely reverse the exceptions of the
earlier program.

catch e as System.Exception
System.Console.WriteLine("In Catch Exception")
catch e as xxx
System.Console.WriteLine("In Catch xxx")

Output
In Catch Exception
This creates a quandary, since the second exception of type xxx shall never get caught. This is
because, the first exception is of type Exception, which will catch all the exceptions.

a.vb
public class zzz
dim shared i as integer = 10
shared sub Main()
try
abc
catch when i <= 5
System.Console.WriteLine("In Catch Exception i <= 5")
catch e as xxx
System.Console.WriteLine("In Catch Exception xxx")
catch e as System.Exception when i > 100
System.Console.WriteLine("In Catch Exception i > 100")
catch e as System.Exception when i = 10
System.Console.WriteLine("In Catch Exception i = 10")
catch
System.Console.WriteLine("In Catch Exception")
end try
end sub
shared sub abc
dim a as System.Exception
a = new System.Exception()
Throw a
end sub
end class
class xxx
Inherits System.Exception
end class

Output
In Catch Exception i = 10

The above example amply substantiates the fact that, a program can contain multiple catch
statements, which can have a combination of both, the 'exception' and the 'when' clause.
However, for the exception to be caught, both these conditions must be satisfied.

The first catch ascertains whether the value contained in variable i is less than or equal to 5.
Since i has been initialized to 10, the condition fails, thereby ignoring the catch for the
exception. The second catch catches all exceptions of type xxx. The condition of this catch
statement also does not get satisfied, since the exception thrown is of type Exception.

The third catch contains both, an Exception type, as well as a 'when' clause. The Exception to
be caught is of type Exception, which evaluates to true. However, the when clause evaluates to
false since i is not greater than 100. As a result, the catch does not get called.

The last catch clause meets both the conditions. Therefore, the final outcome is a value of True.
Thus, we can build as many conditions as we like in a 'catch' statement. However, for an
exception to be caught, all the conditions must be met.

The rationale behind the concept of exception handling is that, it enables us to catch errors
that occur during program execution while allowing the program to continue running. Thus,
every program must have at least one try and catch encompassing the entire code, or else, the
user may encounter the irksome dialog box, which could scare the wits out of him!

Keep in mind that even code written by Microsoft or other software companies could generate
exceptions, which need to be caught.

Exceptions are deemed to be very useful in the context of constructors, since constructors are
incapable of returning a value. Constructors contain considerable amount of code, and if any of
the code fails, there is no mechanism by which the constructor can notify such failure. The
best resolution of such a situation is to throw an exception.

Another convenience of exceptions is when multiple files are opened concurrently in an


application. Each file requires almost identical error checks to be performed on it. No
programmer of sound mind would want to write the same error check multiple times. Hence,
he is bound to take the easy way out by refraining from including error checks altogether. This
could result in fatal errors.

To avoid such a situation, a try-catch statement can be incorporated, which will ensure that all
error handling code is placed in a single location. This simplifies the procedure of error checks
while using multiple files, and it obviates the need to use the same code repetitively.

Visual Basic supports two types of exceptions:


• Structured - which have been explicated earlier.
• Unstructured - which are so named due to their heritage.

The statement 'On Error' has been implemented. However, we will not be explaining this
concept in this book.

The one thing to be borne in mind is that structured and unstructured exceptions cannot be
used simultaeously in the program.

a.vb
public class zzz
dim shared i as integer = 10
shared sub Main()
try
abc
catch e as System.Exception
System.Console.WriteLine(e.HelpLink & "." + e.Message & "." + e.Source & "." + e.StackTrace &
"." & e.TargetSite.ToString)
end try
end sub
shared sub abc
dim a as xxx
a = new xxx("vijay")
Throw a
end sub
end class
class xxx
Inherits System.Exception
public sub new(i as string)
mybase.new(i)
end sub
end class
Output
.vijay.a. at zzz.abc()
at zzz.Main().Void abc()

In the above example, we have endeavoured to print out most of the properties of the Exception
class.

The first property of HelpLink displays a null value. This property is used to locate the help file
that provides greater details about the Exception that is thrown.

The next member is Message, which furnishes some more information about the Exception that
was thrown. The message displayed is normally the string that is passed to the constructor. We
have passed a value of 'vijay' to the xxx constructor. The class xxx is derived from the
Exception class. The first line in the constructor of this class calls the base class and assigns it
the string that has been passed to it. Due to this, the text 'vijay' gets displayed.

If you place the base class call within comments, the Message property will display 'Exception
of type xxx was thrown'. This is the default string assigned to the Message property.

The a.exe application is executed. It has been created from the Visual Basic.Net source file
named a.vb. Due to this, the Source property has been assigned the value of 'a'. When the
Visual Basic.Net compiler is executed using the command vbc /out:b.exe a.vb, the name of the
exe file changes to b.exe, thereby resulting in the Source property now displaying 'b'.

The StackTrace property is responsible for listing the subs that are responsible for throwing the
exception. As mentioned earlier, this list has to be read in a specific sequence. The first line
contains the sub that has thrown the exception. The subsequent subs are the ones that
contain the preceding subroutine.

The TargetSite property is of type MethodBase, which represents a method that throws the
exception. The ToString function displays the signature of the function.

A sub does not return a value, and hence, is not a function. The word Void denotes no return
type. A function which has a return type of void, actually does not return any value at all. This
makes the sub a function with a void return type. Subs are included in Visual Basic.Net
because they were an integral part of the old VB language.
 

 
8. Basics
Before exploring the depths of the Visual Basic language, let us first focus on some core issues.
The first version of Visual Basic was 1.0. We have intentionally mentioned this fact here,
because the software giant Microsoft is not always known to begin all its product versions with
the Version Number 1.0.

Visual Basic was not initially conceived as a language. It was designed mainly as a product
that used the language. Actually, the language was developed as an afterthought, since
Microsoft's main focus was on building the Visual Basic product. Ever since its inception,
Visual Basic has donned the mantle of the most popular language amongst Windows
programmers.

At that stage, the only other competitors in the market were Power Builder and Delphi.
Incidentally, the designer of the above two products has also designed the .Net programming
language named C#. When Microsoft released COM, i.e. the Component Object Model, it used
Visual Basic 4.0 to showcase its efficacy. However, in the current scenario, Visual Basic has
been replaced by Visual Studio.Net. According to the big boys at Microsoft, Visual Basic is
considered to be the easiest entry-point into the Microsoft.Net platform. We are not going to
argue on issue, since we could but not agree with them anymore!
The designers of Visual Basic did not merely extract random ideas out of their hats to develop
this product. They sat down and designed it with a specific target audience in mind. The fact
that the largest number of programmers the world over are Visual Basic programmers is indeed
a tribute to their genius.

To retain the loyalty of this large posse of Visual Basic programmers, Microsoft has very little
choice but to design Visual Basic.Net in such a manner that a Visual Basic programmer would
feel as comfortable using it as a fish in water. If they had not done so, there was imminent
danger of the trillions of Visual Basic programmers jumping ship to some other language ;).
Visual Basic.Net is thus a descendant of Visual Basic.

The syntax and the semantics of the language are easy to understand and the counter-intuitive
features have been circumvented. The language is also designed to be an integral part of the
.Net framework.

All .Net languages compile to IL or Intermediate Language. We are aware of this conversion
since we have written a book on IL. This concept has been introduced to ensure that the
classes created in one language can be used in another language with ease. As a result of this,
there is no way of ascertaining whether the Console class has been written in C# or in Visual
Basic.Net or in some other .Net language.

In order to implement this concept, and to make it fit into the overall framework of the .Net
world, the features of Visual Basic.Net are quite divergent from those of the older Visual Basic.
Thus, Visual Basic.Net is considered to be a reasonable upgrade of Visual Basic. This fact is
also ratified by what is stated in the documentation.

The original designers of Visual Basic had intended it to be a quick and easy language that
people could use, without having any formal training. The emphasis of Visual Basic always was
to facilitate users to build applications with ease. It has succeeded remarkably in its original
intention, a fact that is vindicated from the exceptional success of the product.

However, the language disconcerts the purists, since it infringes the cardinal rules of the
programming world. But hey, who cares? Why fix a thing that already works well?

In Visual Basic.Net, the Enter key signals the end of a line. Most languages such as C, C++, C#
and Java use the semi-colon to signal the end of a statement. The ABAP/4 programming
language from SAP uses the dot, like we do in the English language. However, the designers of
Visual Basic.Net mutually decided to use the Enter key for this purpose.

a.vb
public class zzz
shared sub Main()
System.Console.WriteLine("This line is" &
"broken over two line")
end sub
end class

Error
c:\il\a.vb(3) : error BC30201: Expression expected.

The above program is bound to generate an error, since we are not permitted to press Enter
midway through a line. We unwittingly pressed Enter, since the line was becoming too
protracted. However, this is unacceptable. There is a very simple remedy to this malady, which
is, to use the _ underscore character.

System.Console.WriteLine("This line is" & _


"broken over two line")

Thus, whenever the Visual Basic.Net compiler bumps into the underscore character, it is aware
that the string is incomplete, and continues its search to the next line. Furthermore, we cannot
break up a string enclosed within double quotes. Even an underscore character would not be
able to help in such cases.

The program given below is distinct from those we have encountered so far. It substantiates the
fact that, if and when the reserved words like string and class are used in any other context,
they need to be placed within square brackets.
a.vb
public class zzz
shared sub Main()
System.Console.WriteLine([class].[string])
end sub
end class
class [class]
public shared [string] as integer = 10
end class

Output
10

The program has two classes, zzz and class. String is a shared variable of type integer in the
class. Now, to access the variable in the main subroutine, the syntax to be used is
'class.variable'. Hence, the WriteLine function has a string variable, which is accessed by using
[class].[string].
Mark the use of square brackets. The use of square brackets with the identifier, is also known
as 'escaping' the identifiers.

We would advise you to refrain from using keywords as identifiers, although they are legally
valid, as depicted above. There are only about a hundred such keywords in Visual Basic.Net.
The keywords conform to the Unicode 3.0 Standard Report 15 Annex 5.

Languages such as Chinese and Japanese are extremely visual, and hence, the ASCII character
set, which is used for representing the English language, becomes too restrictive in their case.
The Unicode standard is an approach, by means of which, characters from all languages of the
world can be represented. Unicode, which is an international standard, uses 16 bits, instead of
the regular 8 bits used by the ASCII set.

Another rule for an identifier to adhere to is that, if it begins with an underscore character, it
must have at least one more character following it.

The identifiers have a constraint in the form of their length, i.e. their maximum size can only be
1633 characters. This infuriates us, since only a programmer bereft of all reason, would create
a variable exceeding 100 characters in lengths. Yet, the documentation says that the identifier
length is restricted to the above. Perhaps, in the next version, the limit may be eked out to
32000 characters! Note that the identifiers in Visual Basic.Net are case-insensitive.

a.vb
public class zzz
shared sub Main()
dim a as string
a$ = "hi"
System.Console.WriteLine(a$)
end sub
end class

Output
hi

An identifier or a variable name may be followed by some special characters, such as $ or @.


These special characters are called 'type characters' and they work with non-escaped
identifiers. The variable name mentioned above remains as 'a'. However, the $ sign ensuing it,
indicates that the variable type is a string.

Therefore, the variable is declared in the normal way. The use of 'type' character is optional. It
serves the purpose of visually revealing the data type of the variable.

a@ = "hi"

Error
c:\il\a.vb(4) : error BC30277: Type character '@' does not match declared data type 'String'.

Change the $ sign to @. Its outcome is an error, because the $ denotes a string, whereas, the @
denotes a number or a long. Thus, in spite of the type character being optional, we are not
authorized to use it in inappropriate contexts.
We cannot lodge spaces or white space characters between the variable and the type identifier.
Also, the type characters cannot be placed on entities that do not have a type, such as a
namespace or a classname. Therefore, the example given below, leads to an error, since the
type identifier $ has been positioned in front of the class name.

public class zzz$

Error
c:\il\a.vb(1) : error BC30468: Type declaration characters are not valid in this context.

The keyword mentioned above signifies something special, and has certain rules associated
with it, which need to be followed meticulously. In order to learn any language, we have to
master the use its keywords.

A 'literal' is a constant or a textual representation of some value. We have six different types of
literals. The 'boolean literal' can take one of the two values, True or False.

a.vb
public class zzz
shared sub Main()
System.Console.WriteLine(&Hf)
System.Console.WriteLine(&o12)
end sub
end class

Output
15
10

The integer literal can represent numbers in three different bases. By default, we humans are
habituated into using numbers that are in the decimal notation or numbers with the base 10.

The above example displays numbers in hex. Hence, the number starts with &h. A hex number
is represented, not merely by the digits from 0 to 9, but also by the characters from A to F.
Thus, the digit A stands for 10 and F stands for 15. In the decimal numbering system, each
digit is multiplied with an order of 10, while in hex, it is multiplied by an order of 16.

A number beginning with &o represents an octal number that uses the base of 8. Thus, all
numbers in octal consist of digits from 0 to 7 only. Every individual digit is multiplied by a
order of 8.

While working with computers at the bit level, it is relatively easier to work with the hex and
octal numbering systems, rather than with the decimal system. However, you could spend a
lifetime in the computer world, and yet, never chance upon a number in octal or hex!

a.vb
public class zzz
shared sub Main()
dim i as integer
i = 10000000000000
System.Console.WriteLine(i)
end sub
end class

Error
c:\il\a.vb(4) : error BC30439: Constant expression not representable in type 'Integer'.
You and I may snooze off at the wheel, but the Visual Basic.Net complier does not suffer from
any such malady. It is hawk-eyed. The literal integer has a specific range of values, and if we
exceed it, the above error would get generated.

a.vb
public class zzz
shared sub Main()
System.Console.WriteLine("Hi ""Vijay""")
end sub
end class

Output
Hi "Vijay"

A string 'literal' consists of anything enclosed within double inverted commas, as we had
explained earlier. However, when a double inverted comma is to be inserted in the string, an
additional set of double inverted commas is required to be positioned back to back. The
following characters are used as separators: ( | ) | ! | # | , | . | :.

When the Visual Basic.Net compiler is executed, it first scans the code. This process is known
as 'lexical analysis'. Then, it has an option of including specific code of ours, in the output files
generated. This process is known as 'conditional compilation'. It is demonstrated in the next
example.

a.vb
#const z = true
public class zzz
shared sub Main()
abc
end sub
shared sub abc
#if z then
System.Console.WriteLine("z true")
#else
System.Console.WriteLine("z false")
#end if
end sub
end class

Output
z true

The preprocessor is a program that normally is part of the Visual Basic.Net compiler. It
scrutinizes the code at a very early stage in the whole process. It only looks at lines beginning
with a # symbol, also known as a preprocessor directive.

The preprocessor directive, along with the word 'const', is part of the syntax, which is set to a
value of True. Any value, including numbers, can be assigned here, as long as it is not a string,
since string values cannot be converted into boolean values.

The sub called abc has an 'if' statement, which begins with a # sign, followed by the 'if'
statement. This is the preprocessor equivalent of the 'if' statement. The syntax remains the
same. This directive checks for the value in the 'const'.
The value of z is checked to determine if it is true or false. If it is true, then all the code
between the 'if' and 'else' gets included in the resultant code. But, if the value evaluates to
false, the code between the 'else' and 'end if' gets included. In the above case, the Visual
Basic.Net compiler ignores the 'else' block, thereby totally sidelining the second WriteLine
function.

a.vb
#const z = true
public class zzz
shared sub Main()
abc
end sub
shared sub abc
#if z then
dim i as integer
#else
dim j as integer
#end if
System.Console.WriteLine(i+j)
end sub
end class

Output
c:\il\a.vb(12) : error BC30451: Name 'j' is not declared.

The above example lucidly explains the concept described above. The directive of 'if-else-end if'
is used to create either of the variables i or j, depending upon the value stored in the const z.
Since z holds the value of true, only the variable i is created.
The DIM statement for j gets knocked out of the code, since the 'else' block is snubbed
completely. When the Visual Basic.Net compiler kicks in, the only variable visible to it is i, and
not j. Resultantly, it hurls the error message at us, stating that j has not been declared.

Rewrite the above const statement as :

#const z = nothing

The error message now reads as :

c:\il\a.vb(12) : error BC30451: Name 'i' is not declared.

When the const variable is initialized to nothing, a default value of false is assigned to the
variable. Also, the const variable assumes a default value of false, when the variable has not
been created in the program. If a variable is created, it must be assigned some value, or else,
the following error will be thrown:

c:\il\a.vb(1) : error BC30249: '=' expected.

There are two ways of creating a preprocessor variable; one is by using the const directive, and
the other is by employing an option of the command line compiler. Eliminate the line in the
above program that creates a const variable z, and then, run the Visual Basic.Net compiler
using the command:

vbc /d:z=true a.vb

c:\il\a.vb(11) : error BC30451: Name 'j' is not declared.


The /d option with the Visual Basic.Net compiler creates a preprocessor constant. Thus, a
constant z is created, with its value set to true. The variable j is not defined anywhere, since
the 'else' block has been ignored. Hence, the above error message gets displayed. Had we set
the value of the variable z to false, an error message would have been generated, informing us
that the variable i has not been created.

This method demonstrates the most common use of preprocessor variables. Under normal
circumstances, the program code has a large number of debugging statements sprinkled all
over the program. The example given below delineates this.

a.vb
public class zzz
shared sub Main()
abc
end sub
shared sub abc
#if DEBUG then
System.Console.WriteLine("Debug")
#end if
end sub
end class

There are many things that may go awry while developing programs. Therefore, at every stage,
checks need to be implemented. For this very reason, the code is infused with plenty of
statements, which reveal the activities to the program, while it is running.

The WriteLine function may also be used for this purpose. However, the only failing of this
approach is that, millions of WriteLine functions will have to be deleted, after they have served
their utility.

So, the best solution is to place all the debugging code in a '#if' statement using the variable
DEBUG. At compile time, the DEBUG variable is set to True. On seeing the value of true, all
the WriteLine statements will get executed, thus providing a trace of the events. If the
debugging facility needs to be switched off, i.e. when the program is to be switched to the
Release mode, the DEBUG variable can be deleted or set to False. This would result in all the
WriteLine functions being ignored in the program.

This is just one example of the myriad uses of the preprocessor variables that can be created at
the command line.
 

 
9. Classes, Interfaces and Structures
The most remarkable of all the entities encountered so far is the class, since it creates types
that encompass all the variables/objects and methods within it. Let us devote some time
discerning the various entities that can be placed inside a class.

a.vb
public class zzz
Inherits xxx,yyy
shared sub Main()
abc
end sub
shared sub abc
end sub
end class
public class xxx
end class
public class yyy
end class

Error
c:\il\a.vb(2) : error BC30121: 'Inherits' can appear only once within a 'Class' statement and can
only specify one class.

There are no means by which a class may be derived from two classes simultaneously. This is
not permissible even in the .Net world. Owing to this reason, the above error is shown, since
the class zzz has been derived from two classes viz. xxx and yyy concurrently.

However, the resolution to this quandary would be to derive the class xxx from the class yyy,
and then, derive the class zzz from the class xxx. Thus, it is evident that Visual Basic.Net and
the entire .Net world espouses 'single inheritance' and not 'multiple inheritance'.

In certain situations, when the Visual Basic.Net compiler encounters an error, it loses its poise
and composure, and begins to spew out error messages that are really not errors. Thus, only
the first or second error messages are generally significant; the rest of them can be safely
ignored.

It is imperative to mention the type name after the 'inherits' statement. The 'inherits' keyword
is not a part of the class statement. It is a statement in its own right. Thus, it must be
positioned in the first line after the class statement. The only entity that can follow the inherits
statement is the ordinary comment.

Interfaces

a.vb
public class zzz
shared sub Main()
end sub
end class
public class yyy
Implements xxx
end class
public interface xxx
end interface

The above example does not lead to any errors at compile time, in spite of the introduction of
the new word i.e. 'interface'. Further, the class yyy does not use 'inherits' statement; instead, it
employs the 'implements' statement. After the 'implements' statement, comes the name of an
interface, viz. xxx.
An interface is created in the same manner as a class. If you replace the word 'implements'
with 'inherits' after the definition of the class yyy, it will lead to the following error:

Error
c:\il\a.vb(6) : error BC30258: Classes can inherit only from other classes.

Thus, Visual Basic.Net maintains an unambiguous distinction between an interface and a


class.

a.vb
public class zzz
shared sub Main()
end sub
end class
public class yyy
Implements xxx
end class
public interface xxx
sub abc
end interface

Error
c:\il\a.vb(6) : error BC30149: 'yyy' must implement 'Sub abc()' for interface 'xxx'.

The above example clearly highlights the disparity between an interface and a class. The
interface xxx has a sub abc without the end sub. Also, no code is mentioned after the sub. This
too does not result in any error. However, when the class yyy implements the interface xxx, all
the subs defined in the interface xxx have to be created in the class yyy.

a.vb
public class zzz
shared sub Main()
end sub
end class
public class yyy
Implements xxx
public sub abc implements xxx.abc
end sub
end class
public interface xxx
sub abc
end interface

The above error evaporates since the sub abc has been created with the end sub, in the class
yyy. We have consciously precluded any code in the sub abc. However, this does not generate
any error. Thus, the compiler mainly enforces the condition that, every sub that is created
should contain the end sub. It is not concerned about whether the sub contains any code or
not.

A point to be noted here is that following the keyword 'implements', the name of the interface is
specified, along with the sub whose code is being written. Since we are writing code for the sub
abc, which is present in the interface xxx, the word 'implements' is followed by the sub xxx.abc.
If we uproot 'implements xxx.abc' from sub, we will be confronted by the error shown below:

Error
c:\il\a.vb(6) : error BC30149: 'yyy' must implement 'Sub abc()' for interface 'xxx'.

Using 'implements' with the sub, eases the compiler's job, since it then becomes aware of
which function is being defined in the class. Thus, if an interface has 15 subs/functions, it is
mandatory for all 15 of them to contain code. If the code for even one of them is omitted, an
error will get generated.

a.vb
public class zzz
shared sub Main()
end sub
end class
public class yyy
Implements xxx,iii
public sub abc implements xxx.abc
end sub
public sub abc (i as integer )implements iii.abc
end sub
end class
public interface xxx
sub abc
end interface
public interface iii
sub abc(i as integer)
end interface

The above example has two interfaces xxx and iii, both having a sub definition of abc, but with
different signatures. The sub abc takes one parameter in interface iii. Further, the class yyy
now implements two interfaces. Thus, a class can implement from multiple interfaces, but can
inherit only from one class.

While defining the subs, the 'implements' keyword identifies the sub that is being defined. This
is obligatory, since a class can implement multiple interfaces.

a.vb
public class zzz
shared sub Main()
dim z as zzz
z = new zzz()
z.abc
end sub
sub abc
dim a as yyy = new yyy
dim b as xxx = new yyy
dim c as iii = new yyy
a.abc
a.abc(10)
b.abc
c.abc(100)
end sub
end class
public class yyy
Implements xxx,iii
public sub abc implements xxx.abc
System.Console.Writeline("abc xxx")
end sub
public sub abc (i as integer )implements iii.abc
System.Console.Writeline("abc iii {0}" , i)
end sub
end class
public interface xxx
sub abc
end interface
public interface iii
sub abc(i as integer)
end interface

Output
abc xxx
abc iii 10
abc xxx
abc iii 100

In the example above that is sizeable by any standard, first an instance z of zzz is created, and
then, the sub abc is called off it. In the abc function, three objects, each of a different data
type, are created as instances of yyy. The yyy class implements the two interfaces named xxx
and iii. Therefore, an iii object can be equated with an xxx object.

There are two crucial points that have been purveyed in this program:
• Firstly, an interface is akin to a class, which can be used as a data type.
• Secondly, we can equate base types not only with derived types, but also with interface
types, to classes that implement those types.

However, there is a small caveat here. Rewrite the dim statement that creates an instance of
object b as follows:

dim b as xxx = new xxx

This results in the following error message:

Error
c:\il\a.vb(9) : error BC30375: 'New' cannot be used on an interface.

This is one of the few error messages, which is English-like and easy to understand.

An interface is not permitted to have an instance variable, because an instance of an interface


would never be created. Addition of an instance variable will result in the compiler generating
the following error:
Error
c:\il\a.vb(10) : error BC30602: Interface members must be methods, properties, events, or type
definitions.

Thus, to conclude this discussion, we reiterate that an object can be instantiated of a class,
but not of an interface. However, an interface type can be equated with a class, provided that
the class implements the interface.

Since the object 'a' is of type yyy, the two subs named abc, which are present in the interfaces
of xxx and iii, can be called. The object 'b' is of type xxx; hence, we are only allowed to call the
sub abc from the xxx interface, i.e. the one without any parameters. If we rewrite the line as
b.abc(1000), we get the following error:

Error
c:\il\a.vb(13) : error BC30057: Too many arguments to 'Public Sub abc()'.

The error is obviously expected, since the interface xxx does not have a sub abc that expects
one parameter. The object 'c' of type iii can however make this call, since it has a sub abc that
accepts one parameter.

Now, bring in a small change, i.e. replace the sub abc, which accepts one parameter in the
interface iii, with one that expects no parameters. This will result in making the signatures of
both the abc subs identical. Obviously, the parameter i in the WriteLine function, needs to be
eradicated before making the call. This results in the following error blowing up in the face:

Error
c:\il\a.vb(16) : error BC30269: Method 'abc' has multiple definitions with identical signatures.

This exercise corroborates the point that, in spite of having identical signatures, the objects b
and c can easily identify the call to their abc routine. Nevertheless, it becomes easy for the
compiler to identify which abc is to be called, since they are interface types. However, in case of
object 'a' of type class yyy, the bewilderment persists, since it cannot figure out which
subroutine is to be called, the sub xxx or the sub iii.

a.vb
public class zzz
shared sub Main()
end sub
end class
public class yyy
Implements xxx
public sub abc implements xxx.abc
end sub
end class
public interface xxx
sub abc
end sub
end interface

Error
c:\il\a.vb(12) : error BC30429: 'End Sub' must be preceded by a matching 'Sub'.

While running the above program, we almost believed that we were afflicted with the illness of
the eye, because we stumbled upon an error, despite having clearly specified an 'end' sub
statement with every single sub statement. The error is comes about because an interface
cannot have an 'end' sub, since it is not authorized to contain any code. Thus, the inclusion of
an 'end' sub in the 'interface' causes confusion and generates incorrect error messages.

a.vb
public class zzz
shared sub Main()
end sub
end class
public class yyy
inherits ppp
Implements xxx
end class
public interface xxx
end interface
public class ppp
end class

A class can contain both the statements simultaneously, i.e. of 'inherits' and of 'implements'.
The only restriction here is that inheritance is permitted from only a single class, whereas, no
such restriction is applicable to interfaces.

In certain situations, the Visual Basic.Net compiler is a stickler with regard to the sequence of
the statements. Thus, if we interchange the sequence of 'inherits' and 'implements' as follows:

class yyy
implements xxx
inherits ppp

the following error gets generated:

Error
c:\il\a.vb(7) : error BC30683: 'Inherits' statement must precede all declarations in a class.

Just as certain incidents in life are inexplicable, there exist some rules in programming
languages, which are truly enigmatic. There is no sound reason for insisting on the above
sequence, but who pays any heed to our opinion anyway?! This is the reason why people get
emotional while using idiosyncratic languages.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
public interface bbb
sub abc
inherits ccc
end interface

Error
c:\il\a.vb(7) : error BC30357: 'Inherits' statements must precede all declarations in an interface.

A sub contained in an interface, must have the 'inherits' statement posited at the very start of
the sequence, as in the case of a class. Infringement of this rule is a graver offence than the
fact that the interface ccc does not exist. Maintain the sequence and we assure you that you
will not come to grief.

a.vb
public class zzz
shared sub Main()
end sub
end class
public class yyy
Implements xxx
public sub abc implements xxx.abc
end sub
end class
public interface xxx
public sub abc
end interface

Error
c:\il\a.vb(11) : error BC30270: 'Public' is not valid on an interface method declaration.
Modifiers such as 'public' cannot be used with interfaces. Thus, in an interface that uses a sub
or a function or a property, only the two modifiers of Overloads or Default are permissible.
Modifiers like Public, Private, Friend, Protected, Shared, Static, Overrides, MustOverride or
Overridable are strictly forbidden.

We have not expounded the modifiers Static or Default as yet. But, at times, we may be
compelled to use words that we have not explained, for the sake of completeness and
comprehensiveness of the code.

An interface may contain properties, methods and events, which must be implemented by a
class that derives from it. The ability to create interfaces, is one of the most innovative features
of Visual Basic.Net as compared to the older VB. The older VB was infested with a shortcoming
was that, interfaces could be 'used' or 'consumed', but not created. This also finds a mention in
the documentation.

An interface is a distinct entity from a class, which does not perform any worthwhile role by
itself. It comes into play only when a class decides to implement it. Historically, an interface
represents a contract, wherein a class has to implement all the subs, properties and events
within an interface. If this condition is not met, an error is generated. A class cannot
implement an interface partially. Also, no aspect of an interface can be modified during its
implementation.

a.vb
public class zzz
shared sub Main()
end sub
end class
public class yyy
Implements xxx
public function abc as string implements xxx.abc
end function
end class
public interface xxx
function abc as integer
end interface
Error
c:\il\a.vb(6) : error BC30149: 'yyy' must implement 'Function abc() As Integer' for interface 'xxx'.

In the above example, the function abc returns an integer in the interface xxx, whereas, the
function returns a string in the class yyy. Since we have deviated from the precise definition of
the class, an error is generated.

a.vb
public class zzz
shared sub Main()
end sub
end class
public class yyy
Implements xxx
public function abc as string implements xxx.abc
end function
public function abc as string
end function
end class
public interface xxx
function abc as string
end interface

Error
c:\il\a.vb(6) : error BC30149: 'yyy' must implement 'Function abc() As String' for interface 'xxx'.
c:\il\a.vb(7) : error BC30269: Method 'abc' has multiple definitions with identical signatures.

It is the examples like these, which make this book stand out from the ordinary! The program
has an interface, with a function bearing the same definition or signature, as an independent
function in the class. The sub abc from the interface xxx and an independent sub abc, are both
defined to return a string.

The error was anticipated for reasons that have been stated earlier. However, the error message
that is emitted is very different from what we had expected. The compiler first examines the
independent sub abc. It searches for the keyword 'implements' after the sub name. When it is
unable to locate it, the compiler flags an error.

The compiler then encounters the sub abc containing the 'implements' clause. It confesses its
imprudence, since it did not perform this check earlier, and then, it generates an error. Just as
it is almost impossible to fathom how a woman's mind works, it is equally impossible to figure
out how the compiler decides on the error message to be displayed.

We once again reiterate that interfaces are like contracts, once implemented, cannot be
modified. Further, they have to be implemented in full. The logic behind this is that any
amendments made to the interfaces, will disrupt the code when the program is recompiled,
since the class has no mechanism to implement the new changes.

The program stipulated below has nothing to do with interfaces, but it demonstrates the
necessity for coding in the appropriate manner.

a.vb
public class zzz
dim a as yyy
shared sub Main()
a = new yyy
end sub
end class
public class yyy
end class

Error
c:\il\a.vb(4) : error BC30369: Cannot refer to an instance member of a class from within a shared
method or shared member initializer without an explicit instance of the class.

The above example generates an error because of the word 'shared'. Since the sub Main is
specified as 'shared', it avoids creating an instance of the zzz class.

But, the object 'a' is an unshared or normal object. To access it, an instance of class zzz is
required. This results in a dichotomy. As a result, instance variables in a sub that is 'shared',
cannot be used in spite of instantiating the object 'a'. The other way around works fine, i.e. we
are allowed to use shared variables in 'shared' or instance subs.

Moreover, the above rule does not apply to local variables, i.e. if the dim statement had been
made local or placed inside the sub, no errors would have been generated at all.

a.vb
public class zzz
shared sub Main()
dim z as zzz
z = new zzz()
z.abc
end sub
sub abc
dim a as yyy = new yyy
dim b as xxx = new yyy
dim c as iii = new yyy
a.abc
b.abc
c.abc
end sub
end class
public class yyy
Implements xxx,iii
public sub abc implements xxx.abc , iii.abc
System.Console.Writeline("abc")
end sub
end class
public interface xxx
sub abc
end interface
public interface iii
sub abc
end interface

Output
abc
abc
abc
The above example is a modification of the earlier one, wherein the class yyy implements from
two interfaces, viz. iii and xxx, each having a sub abc. This by itself would result in an error.
Therefore, the class yyy has an implements class with sub abc, which contains both the
interface names. In this manner, the compiler is notified that, sub abc in the class is the
implementation of both the abc subs from the interfaces.

This leads to a single implementation for the sub abc, in the case of both the interfaces. There
is no ambiguity as to which sub abc is to be called. Therefore, no error is visible. An error is
flagged only when the compiler is confused as to which sub is to be called, from amongst
multiple subs of the same ilk.

However, if the class yyy implements the interface jjj containing a sub abc, and then it writes
out the code of the sub abc along with the implements of both the interfaces, a large number of
errors will be generated. This is because the object of type yyy, will now be in a dilemma as to
which sub abc to call, and it would not call the individual interface types.

Thus, the implements can accept multiple interface names that are comma separated. It can
also accept multiple interfaces with sub names, when used along with a sub. What applies to a
sub is also applicable to a function, a property and an event, unless otherwise specified.

a.vb
public class zzz
shared sub Main()
end sub
end class
public class yyy
Implements xxx
public shared sub abc implements xxx.abc
System.Console.Writeline("abc")
end sub
end class
public interface xxx
sub abc
end interface

Error
c:\il\a.vb(7) : error BC30505: Methods or events that implement interface members cannot be
declared 'Shared'.

The above example clearly demonstrates the fact that while implementing an interface method,
the shared attribute cannot be employed. This is because the word 'shared' binds the sub to
the class, and not to the interface. The attributes Overloads, Overrides, Overridable, Public,
Private, Protected, Friend, Protected Friend, MustOverride, Default, and Static may be used
instead.

Using the 'private' access modifier restricts access to the members of the same class. Individual
instances are not permitted to use the member.

a.vb
public class zzz
shared sub Main()
end sub
end class
public interface xxx
sub abc
end interface
public interface iii
inherits xxx
sub pqr
end interface

The above example does not generate any error, since we can have an interface iii that
'inherits', but not 'implements' from another interface. Using the keyword 'implements' in lieu
of 'inherits' will generate an error. Thus, any class that implements interface iii, will then have
to implement both the subs named abc and pqr.
a.vb
public class zzz
shared sub Main()
end sub
end class
public class yyy
implements iii
public sub abc implements iii.abc
end sub
public sub pqr implements iii.pqr
end sub
end class
public interface xxx
sub abc
end interface
public interface iii
inherits xxx
sub pqr
end interface

Implement interface iii will in no way disclose the interfaces that 'iii' has been inherited from.
At the same time, this information is always made available. Thus, while implementing sub
pqr, there is no way to figure out whether it originates from interface iii or from xxx. However,
changing the implements in sub pqr, from iii.pqr to xxx.pqr, will certainly generate an error, in
spite of it being technically accurate.

When the name of the sub is changed from pqr to abc in the interface iii, the error displayed
below is flashed. This is because two subs with the same name in a class, cannot dwell
together. The problem of duplicity in names has been touched upon earlier.

Error
c:\il\a.vb(9) : error BC30583: 'iii.abc' cannot be implemented more than once.

Interfaces are considered to be a useful programming tool, as they separate the definitions of
objects from their actual implementation. This allows objects to evolve, without incurring the
risk of their breaking down. The whole world of COM or the Component Object Model lays
emphasis on the interaction between binary objects, which can evolve individually over a period
of time, and yet interact seamlessly. This can only happen by employing the concept of
interfaces.

A class implementing an interface, publicly claims to have conformed to certain rules. This is
because, the class defines the subs declared in the interface. Most importantly, interfaces have
resolved an outstanding predicament of a fragile base class, which occurs when the base class
is modified.
Interfaces score over classes in their utility, because a class can derive from many interfaces,
and yet have a single implementation.

a.vb
public class zzz
shared sub Main()
end sub
end class
public class yyy
implements xxx
Event eee(ByVal e As string ) Implements xxx.eee
Function abc As String Implements xxx.abc
End Function
Property pqr() As Integer Implements xxx.pqr
Get
End Get
Set
End Set
End Property
end class
Interface xxx
Function abc As String
Property pqr() As Integer
Event eee(a As string)
End Interface

In order to deal with this topic comprehensively, we have created an interface xxx with a
function, a property and an event. The interface is implemented by class yyy. This program
achieves nothing new, other than demonstrating that properties, Events and functions too can
be implemented.

a.vb
public class zzz
shared sub Main()
end sub
end class
public class yyy
implements iii
end class
public interface xxx
end interface
public interface iii
inherits xxx , uuu
end interface
public class uuu
end class

Error
c:\il\a.vb(11) : error BC30354: Interface can inherit only from another interface.

In the above example, the interface iii inherits from the interface xxx, as well as, from the class
uuu. Candidly speaking, we didn't expect an error to occur, since we thought that, whatever
worked for a class would also work for an interface.
Man proposes and the Visual Basic.Net compiler disposes! The error indicates the fact that an
interface can only inherit from another interface, and not from a class. It makes sense, since
an interface cannot embody code.

We would have preferred to use the word 'implements' in place of 'inherits' for the sake of
consistency. If you change uuu to an interface, the error disappear.

a.vb
public class zzz
shared sub Main()
end sub
end class
public interface iii
implements uuu
end interface

Error
c:\il\a.vb(6) : error BC30604: Statement cannot appear within an interface body. End of interface
assumed.

The compiler is very stringent about what is allowed to be placed in an interface. Providing the
wrong keywords such as 'implements', is bound to generate the above generic error.

public interface iii

Error
c:\il\a.vb(1) : error BC30253: 'Interface' must end with a matching 'End Interface'.

end interface

Error
c:\il\a.vb(1) : error BC30252: 'End Interface' must be preceded by a matching 'Interface'.

Henceforth, we will show you the above set of matching errors, which occur for every statement
such as sub, interface etc., which contain a matching end statement.

Structures

Before we delve into the innards of a structure, let us attempt the program given below.

a.vb
public class zzz
shared sub Main()
end sub
end class
structure sss
end structure

Error
c:\il\a.vb(5) : error BC30281: Structure 'sss' must contain at least one instance member variable
or Event declaration.

The above error message clearly specifies that at least one variable or instance member should
be present within a structure. A class can be empty, but not a structure.
a.vb
public class zzz
shared sub Main()
dim a as sss
a = new sss
System.Console.Writeline(a.i)
a.i = 10
System.Console.Writeline(a.i)
a.i = a.i + 100
System.Console.Writeline(a.i)
end sub
end class
structure sss
dim i as integer
dim j as string
end structure

Output
0
10
110

In the above program, two variables have been added to the structure definition. Then, in the
sub main, an object 'a' of type sss has been created. No errors are witnessed because, akin to a
class, a structure may also be used to create new data types. Furthermore, the keyword of
'new' can be used to instantiate a new instance of a structure. All members of the object are
assigned a default value of zero, when the 'new' keyword is used to create it.

We are allowed to access the members of the structure by using the dot as a separator,
between the name of the structure variable and the structure member. Apart from this, a new
value too can be assigned to the member. However, the member name cannot be used directly.
It has to be preceded by the structure variable and a dot. A structure variable can be used on
both, the left and the right hand side of the 'equal to' sign.

a.vb
public class zzz
shared sub Main()
end sub
end class
structure sss
dim i as integer = 10
end structure

Error
c:\il\a.vb(6) : error BC31049: Initializers on structure members are valid only for constants.

Although there are many concepts that are common between classes and structures, there still
exist a few disparities. For example, an instance variable cannot be initialized in a structure,
whereas, this is possible in a class. Replacing the word 'structure' with 'class' will wipe away
the error thrown earlier.

We now present a few programs that will take a closer look at the similarities and differences
between structures and classes.

a.vb
public class zzz
shared sub Main()
dim a as sss = new sss
a.abc
end sub
end class
structure sss
dim i as integer
sub abc
System.Console.WriteLine("hi")
end sub
end structure

Output
hi

The above example reveals that we are permitted to place code in a structure, even though it is
not a very good idea to do so.

a.vb
public class zzz
shared sub Main()
dim a as sss = new sss
'a.abc
end sub
end class
structure sss
dim i as integer
sub new
System.Console.WriteLine("hi")
end sub
end structure

Error
c:\il\a.vb(9) : error BC30629: Structures cannot declare a non-shared 'Sub New' with no
parameters.

The above program bars the use of a 'constructor with no parameters' in a structure. However,
we can have constructors in a structure, provided that they are passed with parameters. The
next program showcases this fact.

a.vb
public class zzz
shared sub Main()
System.Console.WriteLine("Begin")
dim a as sss = new sss(100)
dim b as sss = new sss()
end sub
end class
structure sss
dim i as integer
sub new(j as integer)
System.Console.WriteLine(j)
end sub
shared sub new()
System.Console.WriteLine("Shared")
end sub
end structure

Output
Begin
Shared
100

The above example clarifies certain concepts about structures and shared constructors. In the
above program, we have created a constructor, which takes an integer as a parameter.
Furthermore, an instance of the sss object is created and passed the number 100 as a
parameter. This leads to it receiving a free constructor with no parameters, since we are
allowed to create an sss object without passing any parameters to 'new'. The member i of the
structure, need not be initialized at all.

Finally, we are allowed to have a constructor with no parameters, provided it is marked as


'shared'. The next example inquires into 'shared' in greater detail.

a.vb
public class zzz
shared sub Main()
System.Console.WriteLine("Begin")
sss.i = 10
end sub
end class
structure sss
shared dim i as integer
dim j as integer
shared sub new()
System.Console.WriteLine("Shared")
end sub
end structure

Output
Begin
Shared

A 'shared' constructor is always called before the object gets created. On the other hand, if the
object is not created, the shared constructor never gets called. However, it gets called if a
shared member of the structure is accessed. In the above program, the sss object is not created
at all, but since the shared member i is accessed, the shared constructor gets called.

There ought to be total equality for all in this world!. Based on these lines, a class or structure
can contain either shared or instance members. An instance constructor is required for the
instance members, and a shared constructor is deemed necessary for the shared members.

The structure must contain at least one instance member, or else, an error crops up. If you
remove the line "dim j as integer", the following error message will be displayed:

Error
C:\il\a.vb(7) : error BC30281: Structure 'sss' must contain at least one instance member variable
or Event declaration.
A short while ago, we had learnt that an instance variable cannot be referred to in a shared
sub, and if this is done, an error is thrown.

A structure is like a class, where different types are grouped together into a single entity.
Legend has it that the concept of structures was invented by Kerningham and Ritchie, the
inventors of the C programming language. It was so because, without structures, they were
unable to use the C programming language to write the Unix operating system.
A structure, like a class, is a composite data type, since it contains individual data types. These
individual members in turn, could also be structures and classes. The next example
substantiates this concept.

a.vb
public class zzz
shared sub Main()
dim a as new sss
a.j = 10
a.k.i = 20
System.Console.WriteLine("{0} {1}", a.j,a.k.i)
end sub
end class
structure ttt
dim i as integer
end structure
structure sss
dim j as integer
dim k as ttt
end structure

Output
10 20

In this program, there exists a structure within another structure. The structure ttt or UDT
(user-defined-type), contains an integer member i. This type is now used in another structure
sss, which contains j as an integer member, and k as a ttt type member.

In order to access the members of the structure, the dot is used as a separator between the
structure name and member name. Therefore, to refer to the member j, the structure name 'a'
is followed by a dot, which is followed by the member name 'j'. Thus, the resultant expression
is a.j. Similarly, to refer to k, the notation of a.k is used. However, the structure name k has no
meaning, since we are interested in accessing i. Therefore, we follow up k with another dot and
then insert the name of the member i, to arrive at the final expression of a.k.i.

A structure can internally be comprised of many more members. This implies that the member
i could also have been a structure. This structure in turn, could have properties and events.
The access modifiers also behave in a similar fashion.

dim a as sss

We have effected only a single change in the above program, and that is, within the dim
statement, we have removed the word 'new', which instantiates the structure. No errors are
encountered here. This is because, like an integer type, there is no essentiality to use the word
'new' with structures, since it is a value type. It is optional to use the keyword 'new' with
structures.

a.vb
public class zzz
shared sub Main()
dim a as new sss
a.j = 10
a.k.i = 20
a.l.i1 = 30
System.Console.WriteLine("{0} {1} {2}", a.j,a.k.i, a.l.i1)
end sub
end class
public class yyy
public dim i1 as integer
end class
structure ttt
dim i as integer
end structure
structure sss
dim j as integer
dim k as ttt
dim l as yyy
end structure

Error
Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of
an object.
at zzz.Main()
In this program, the rules that are implemented on the structure, have also been used on a
class. We introduced one more object l, of type yyy, and a class with one member named i1. We
used the same 'dot within dot' notation to access the member i1, and yet, we got no compiler
error.

This signifies that syntax-wise, a structure and a class are similar. But, when we run the
program, the above exception gets thrown.

The runtime error is generated because the reference type is not instantiated. So, modify the
code to include the following:

a.k.i = 20
a.l = new yyy
a.l.i1 = 30

Output
10 20 30

Thus, the object l is now instantiated as a new instance of yyy type, by using the keyword
'new'. The compiler should have complained, but it did not.

Structures are automatically allocated memory, and are useful when related information about
a particular entity is to be grouped together.

a.vb
public class zzz
shared sub Main()
dim a as new sss
end sub
end class
public class yyy
public dim i1 as integer
end class
interface ttt
sub abc
end interface
structure sss
implements ttt
dim j as integer
sub abc implements ttt.abc
end sub
end structure

A structure is also allowed to implement an interface. Thus, in the above program, the
structure sss is extended to contain an interface ttt implementation. The interface has only one
sub abc, which must be defined in the structure, to avoid an error. Thus, for the 'implements'
statement, a structure and a class behave similarly.

The access modifiers of a structure consist of the familiar gang of four: public, private,
protected and friend. They work in the same manner as discussed earlier. The default
accessibility for a structure is 'public'.

Every variable created in a structure must have an access modifier specified for it. Omitting it
will throw an error. The default modifier for a DIM statement in a structure is 'public'. As a
result, the modifier can be skipped when a DIM statement is used in a structure.

The next set of examples will illustrate a major difference between a value type and a reference
type. As indicated earlier, a value type is a simple type, like an integer, or a type created by a
structure. The reference types encompass all the other types.

a.vb
public class zzz
shared sub Main()
dim a,b as sss
a.j = 10
b=a
System.Console.WriteLine(b.j)
b.j = 100
System.Console.WriteLine(a.j)
end sub
end class
structure sss
dim j as integer
end structure

Output
10
10

In the Visual Basic.Net program, two objects a and b of value type sss have been created. Then,
the member 'j' of object 'a' is initialized to a value of 10. The members with the object 'b' have
not even been touched. On the next line, the entire structure 'b' is initialized to the structure
'a'.
Consequently, all members of the structure 'b' are assigned the values of the corresponding
members of the structure 'a'. Thus, the member 'j' of structure 'b' gets equated to 10, which is
the equivalent value of the member 'j' in structure 'a'. Therefore, the first WriteLine function
displays a value of 10.

Continuing with our experiments, we then equate the member j of the structure b to 100, and
then, display the member j from the structure 'a'. However, the value remains 10, thus
displaying no signs of any change, whatsoever. This is because the change was incorporated in
a separate entity 'b', which has no linkages with 'a'. This goes to prove that the value members
of the structure are independent of each other.

a.vb
public class zzz
shared sub Main()
dim a,b as sss
a.j = new yyy
a.j.i = 10
b=a
System.Console.WriteLine(b.j.i)
b.j.i = 20
System.Console.WriteLine(a.j.i)
end sub
end class
structure sss
public dim j as yyy
end structure
public class yyy
public dim i as integer
end class

Output
10
20

The structure sss is now modified to contain a reference type variable 'j' of type class yyy. Then,
the variables 'a' and 'b' of type sss are created, using the DIM statement. However, this does
not instantiate the yyy object, since it has to be done explicitly for each of them.

Next, using the structure 'a', the variable j of type yyy is instantiated, and the variable i in the
class is initialized to a value of 10. The member j in the structure b remains un-initialized, and
hence, it does not have any value.

A value type represents an actual value, whereas a reference type stores a number or a
reference to the location where the actual object is stored in memory. If we try to print the
value of the object j, the resultant effect will be that it will actually point to the memory location
where the value of the integer 'i' is stored, and hence, the stored value will get printed.

On using the new yyy statement, the system allocates some memory to store the members of
the class yyy. If this memory is allocated at the location of 100, the value of the object j will be
100. So, to access the member i of the class yyy, a two-step procedure is adopted: firstly, the
location at which object begins in memory i.e. 100, is to be accessed. Secondly, from this
memory location of 100, the value of the member i has to be picked up.
Being a two-stage process, it is slower than the value type process that directly contains the
value. Thus, we can retrieve values faster from structure types or value types, rather than from
reference types.

Imagine as to the pace at which the .Net world would crawled, had the integer type also been
made as a reference type !
Like in the previous example, the structure b is equated to the structure a. Doing so assigns a
value of 100 to the member j in structure b. The end result is that the j member, in each of the
two structures, points to the same yyy object.

The WriteLine function displays the member j from object b. The value shown is the same as in
object a. Changing the value of member i using b will now change the value in object a, since
both point to the same yyy object.

a.vb
public class zzz
shared sub Main()
dim a,b as sss
a.j.i = 10
b=a
System.Console.WriteLine(b.j.i)
b.j.i = 20
System.Console.WriteLine(a.j.i)
end sub
end class
structure sss
public dim j as yyy
end structure
structure yyy
public dim i as integer
end structure

Output
10
10

By merely incorporating two small modifications in the program, the output changes
dramatically. The structure sss now has the member j declared as a value type, and not as a
reference type, since 'class yyy' is changed to 'structure yyy'. Then, the member i of structure
yyy is initialized to 10, using the structure 'a'.

Since it is a structure, there is no need to create a new instance of the structure. Also, equating
the two structures merely results in the assigning of the values to the corresponding members
in the structure. No references are stored. Thus, when the value i is displayed, it shows a value
of 10. Changing its value to 20 does not affect the other structure, since each of them now
behaves as an independent entity. Bear in mind that internally, integers are also structures.

a.vb
public class zzz
shared sub Main()
dim a as sss
a.j = 10
abc(a)
System.Console.WriteLine(a.j)
end sub
shared sub abc(byval s as sss)
s.j = 100
end sub
end class
public structure sss
public dim j as integer
end structure

Output
10

A structure behaves like a value type, even while dealing with parameters. In the above
example, a structure 'a' of type sss has the 'j' member set to 10. Then, the shared sub abc is
called and passed the structure as a parameter.

The sub abc stores the structure in the variable 's', and assigns it the modifier 'byval'. This
modifier ensures that structure 's' is totally disconnected from the structure 'a'. Thus, when
the member j in the sub abc is changed to 100, this change does not get reflected in the
original structure. The value of j in sub main remains 10. This has been verified using the
WriteLine function.

Now, replace the modifier byval with byref in the sub abc as follows:

shared sub abc(byref s as sss)

Output
100

The modifier byref with the parameter now represents the actual object, and not a copy. Thus,
any changes made to the object 's' will be instantly reflected in the object 'a' in the sub main.

The variable j remains the same in the sub abc, as well as in the sub main. Therefore, if we
change its value to 100, the same value gets reflected in the sub main also.

a.vb
public class zzz
shared sub Main()
dim a as new sss
a.j = 10
abc(a)
System.Console.WriteLine(a.j)
end sub
shared sub abc(byval s as sss)
s.j = 100
end sub
end class
public class sss
public dim j as integer
end class

Output
100
In this example, sss is made a class, instead of a structure. Thus, in the DIM statement, it has
to be initialized using the 'new' statement. Then, the j member is set to 10 and the sub abc is
called.

The parameter s, which is provided to the sub, has the modifier of byval. Thus, the value
assigned to object a is also passed on to the object s. Effectively, the object s points to the same
location as the object a.

Thus, any change to the member j in the object s will be reflected in the main subroutine. This
is because, s is a reference object, and hence, it points to the same memory location referenced
by the object a. So, for a reference object, it does not make any difference whether byval or
byref is used, since any change made in the object invariably gets reflected in the original.

a.vb
public class zzz
shared sub Main()
dim a as new sss
a.j = 10
abc(a)
System.Console.WriteLine(a.j)
end sub
shared sub abc(byval s as sss)
s = new sss
s.j = 100
end sub
end class
public class sss
public dim j as integer
end class

Output
10

The above example explains the difference between byref and byval in the context of reference
types. We retain the program as before, and now, simply initialize s to a new sss object. Then,
we set the value of the member j to 100.

By using 'new', the parameter 's' that contains a reference to the object 'a' in memory, now
holds a reference to a new sss object. This new sss object is different from the object 'a' in
main, as a result of which, any changes made to the object s will not be reflected in the object
'a'. In all, there are two sss objects in memory, viz. 's' and 'a'. The WriteLine function uses the
object 'a' to display the value of 'j'. Hence, the output is shown as 10.

Now, change byval to byref as shown below:

shared sub abc(byref s as sss)

Output
100

The output changes to reflect the new value assigned to 'j', because any changes made to the
parameter 's' will simultaneously result in a similar modification to the original object 'a'. Thus,
assigning a new memory location to 's' will also change the location of 'a'. As a result of this,
the system loses track of the earlier copy of sss. This explains why WriteLine displays a value of
100, and not 10.
a.vb
public structure sss
public dim j(10) as integer
end structure
public class zzz
shared sub Main()
end sub
end class

Error
c:\il\a.vb(2) : error BC31043: Arrays declared as structure members cannot be declared with an
initial size.

In this program we have attempted to create an array in the structure. This is permissible, as
long as the size is not specified in the array. Since the program mentions the size of the array,
the above error is thrown.

a.vb
public structure sss
public dim j() as integer
end structure
public class zzz
shared sub Main()
dim a as sss
redim a.j(2)
a.j(0) = 10
a.j(1) = 20
System.Console.WriteLine(a.j(0) + a.j(1))
end sub
end class

Output
30

The above example places things in the right perspective. The structure sss has an array
variable j of type integer, with no size mentioned.

In Visual Basic.Net, arrays have to be created without specifying the size. This feature makes it
easier to specify the size at a later stage. The 'redim' keyword can be used, along with the array
name and a new size, to modify the dimensions of any array. There is no need to re-create the
array for the variable, since the redim statement performs this job.

Thus, it can be seen that the creation of an array in a structure is a two-step process. The
array j has two members, which are set to the values of 10 and 20 using the dot notation,
respectively. The values are then summed up and displayed.

If we bypass the redim statement, it will lead to non-creation of the array. Thus, the array
member j would be set to nothing. This does not generate any compilation errors, but at run
time, an exception will be thrown. Unlike structures, in the case of classes, the array size can
be specified at the time of creation of the array. The next program proves this point.

a.vb
public structure sss
public dim j as integer
end structure
public class zzz
shared sub Main()
dim a(4) as sss
a(0).j = 10
a(1).j = 100
System.Console.WriteLine(a(0).j + a(1).j)
end sub
end class

Output
110

Any data type can be converted into an array. An array is simply a collection of any type. The
program has an array 'a' containing four sss structures. A value data type needs no
initialization at all. Therefore, the members of a(0), a(1) etc., which actually belong to an array
of structures, now behave like an array of integers.

In all the .Net languages, the syntax of structures and classes looks identical. It is very difficult
to decipher whether the entity is a class or a structure, by merely looking at the definition.

A structure, like a class, can contain constructors, methods, properties, fields, events,
constants and enums. Furthermore, it can implement 'interfaces' and have 'shared'
constructors, with or without parameters.

Before we delve on the numerous differences between structures and classes, we would like to
summarize all the concepts that have been explained earlier in this chapter.

The .Net world comprises of two basic data types: value types and reference types. Structures
are used to create user-defined value data type, whereas classes are used to create reference
types. There is no other way of creating types in the .Net world. The value types are simple and
fast, compared to the reference types.

All variables created in a function or passed as parameters, are stored in an area of memory
called the 'stack'. This memory gets recycled for every function. Thus, variables and parameters
in the functions lose their values at the end of a function call. The instance variables are
allocated memory in a separate section called the heap area, and they exist till the program is
alive.

A structure is allocated memory on the stack, while classes are placed in the heap area. The
default access modifier for structure members is 'public', whereas, for classes it is the reverse,
i.e. 'private'. This applies only to class variables and constants. Everything else is like a
structure, which has a 'public' modifier. This unusual behaviour is implemented to maintain
compatibility with Visual Basic 6.0.

a.vb
public structure sss
inherits ddd
public dim j as integer
end structure
public class zzz
shared sub Main()
end sub
end class
Error
c:\il\a.vb(2) : error BC30628: Structures cannot have 'Inherits' statements.

A structure can implement an interface, but it cannot inherit from a class. It is for this reason
that the above error gets generated, since the structure sss inherits from the class ddd.

A point to be noted here is that, there is no class named ddd in the code. However, the
compiler overlooks this, and throws an error message only in respect of the 'inherit' flaw. This
is because, the compiler stops processing as soon as it encounters the first mistake.

Thus, it is preferable to use structures only for variables, since they cannot receive code from
any class.

a.vb
public class yyy
inherits sss
end class
public structure sss
public dim j as integer
end structure
public class zzz
shared sub Main()
end sub
end class

Error
c:\il\a.vb(2) : error BC30299: 'yyy' cannot inherit from structure 'sss' because 'sss' is declared
'NotInheritable'.

The program shows class yyy inheriting from a structure sss. Based on our assumption, this
should work fine. However, we get an error. This is because, the structure has been implicitly
given a modifier of NotInheritable. The next example illustrates the use of the NotInheritable
keyword.

a.vb
public class yyy
inherits sss
end class
public NotInheritable class sss
end class
public class zzz
shared sub Main()
end sub
end class

Error
c:\il\a.vb(2) : error BC30299: 'yyy' cannot inherit from class 'sss' because 'sss' is declared
'NotInheritable' .

The world is full of sly people, who believe that they have absolute right over what belongs to
the others. This feature is visible in programming too. Programmers spend all their time and
effort in creating classes, which they may not want others to use. So, to prevent others from
deriving from the class, the keyword of NotInheritable is used. This is in absolute contrast to
the code-reuse concept, wherein coders openly distribute their code for use by others.
In the above program, class sss is defined to be NotInheritable, thereby disallowing any class
from deriving from it. Since the class yyy derives from it, the rule gets desecrated, resulting in
the above error. By default, the NonInheritable keyword gets added to a structure.

public NotInheritable structure sss


public dim j as integer
end structure

Error
c:\il\a.vb(1) : error BC30395: 'NotInheritable' is not valid on a Structure declaration.

We are never allowed to specify the NotInheritable class for a structure. If we do, an error
stating the obvious is shown, because this is simply unacceptable to the compiler.

a.vb
public structure sss
protected dim j as integer
end structure
public class zzz
shared sub Main()
end sub
end class

Error
c:\il\a.vb(2) : error BC30435: Members in a Structure cannot be declared 'Protected'.

As a structure cannot be derived from, it makes no sense for a member of a structure to be


declared as 'protected'. To refresh your memory, 'protected' allows only the derived classes to
use the members, and unfortunately, the structure cannot be used as a derived class.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
public structure yyy
Event e()
Sub vijay() Handles c.e
End Sub
end structure

Error
c:\il\a.vb(7) : error BC30728: Methods declared in structures cannot handle events.

In structures, event handling imposes too many restrictions. The above error occurs since a
sub 'vijay' in structure yyy handles an event named c.e.

The error clearly mentions the fact that methods in a structure cannot handle events. We do
not get any due to the absence of the object c in the code. Also, the presence of the event e
seems to escape detection.

a.vb
Public Class zzz
WithEvents b as yyy
Shared Sub Main()
End Sub
End Class
public structure yyy
dim a as integer
end structure

Error
c:\il\a.vb(2) : error BC30413: 'WithEvents' variables cannot be typed as structures.

In order to work with events, a variable in the class containing the event has to be instantiated.
In the above code, since the type passed to the WithEvents clause is a structure, the compiler
complains by displaying an error message. Event handling activities must be avoided in the
case of structures.

Structures implicitly inherit from the class ValueType. However, as said before, we cannot
explicitly specify the 'inherits' keyword with a structure.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
public class yyy
inherits System.ValueType
end class

Error
c:\il\a.vb(6) : error BC30015: Inheriting from 'System.ValueType' is not valid.

A class cannot derive from ValueType. If this is done, the above error message will be flagged. A
structure has a free constructor, which takes no parameters and initializes all the members of
the structure to their default values. This behaviour cannot be modified manually by any
programmer.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
public structure yyy
dim a as yyy
end structure

Error
c:\il\a.vb(5) : error BC30293: Structure 'yyy' cannot contain an instance of itself:

A member of a structure cannot refer to itself. The above program displays an error message
because, the structure yyy in turn has a member 'a' of type yyy. This is not allowed. However,
this is permissible in a class. This can be proved by modifying the word 'structure' to 'class'.

This is because, a structure variable is bound to its values, whereas a class variable refers to
the class in memory. Thus, multiple class variables can refer to the same class in memory.
Also, when a statement of 'dim a as sss' is encountered, where sss is a structure type, the
compiler rewrites the code as follows:
dim a as sss
new sss
or
dim a as new sss.

Take your pick. If you write the DIM statement as shown above, the Visual Basic.Net compiler
will be most grateful, since this would have saved it some time. It does not give us any error
either.

a.vb
Public Class zzz
Shared Sub Main()
dim a as new yyy
a.i = 10
a.abc
a = nothing
a.abc
End Sub
End Class
public structure yyy
public dim i as integer
public sub abc
System.Console.WriteLine(i)
end sub
end structure

Output
10
0

The program starts by creating a structure 'a' using the 'new' keyword, which is optional. Then,
the member i is initialized to 10, and the sub abc is called. Everything works as per plan, with
the sub abc printing the value of i as 10.

After printing the value, the structure variable 'a' is initialized to nothing. This keyword is
special, since it sets all the members of the structure to their default values. Thus, the value of
the integer variable is set to 0. The sub abc cannot be set to any value, since subs cannot have
values. Hence, it is left alone. The result of this operation leads to the display of the value of i
as 0 in abc.

Now re-run the same program again after introducing the following modification. Replace the
two instances of the word 'structure' with the word 'class'. Doing so will not generate any
complaints from the compiler. But at run-time, the following exception is thrown:

Output
10
Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of
an object.
at zzz.Main()

The first sub does its job of displaying the value of 10. But after initializing the class to
nothing, the exception gets thrown. This is because 'nothing' nullifies all accesses to the
members in the object. Accessing the member i using nothing, is bound to generate an error.
Thus, setting a structure to nothing resets the members of the structure to a default value. On
the contrary, all the members in a class become inaccessible. The only solution is to use 'new'
to create another instance.

a.vb
Public Class zzz
Shared Sub Main()
dim a as new yyy
dim b as new yyy
System.Console.WriteLine(a is b)
b=a
System.Console.WriteLine(a is b)
End Sub
End Class
public class yyy
public dim i as integer
public sub abc
System.Console.WriteLine(i)
end sub
end class

Output
False
True

The above program focuses on classes, and not on structures. The main intention here is to
compare two objects of type yyy, and determine whether they point to the same object or not. If
they do, then they are considered to be equal. To check for equality, the 'is' operator is used in
place of the = operator.

The 'is' operator returns a bool value. In the first case, since both 'a' and 'b' point to separate
objects in memory, the output shows a bool value of false. Equating object 'b' to 'a' compels
both the objects to point to the same memory location. Hence, the return value is shown as
true.

Now replace the word 'class' with 'structure'. Doing so will generate the following compiler
error:

Error
c:\il\a.vb(5) : error BC30020: 'Is' requires operands that have reference types, but this operand
has the value type 'yyy'.

Structures are considered to be value types, and the operator 'is' acts only on reference types.
Replacing the 'is' operator with the 'equal to' sign for the purpose of comparing two structures,
will result in the following error:

Error
c:\il\a.vb(5) : error BC30452: Operator '=' is not defined for types 'yyy' and 'yyy'.

Thus, the only way to determine the equality of two structures is to compare each and every
member individually. There are no short cuts here. The structure statement can appear either
in a module, or in a namespace, or at the file level. A structure cannot be created in a method.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
public class yyy
public sub abc
structure aaa
end structure
end sub
end class

Error
c:\il\a.vb(7) : error BC30289: Statement cannot appear within a method body. End of method
assumed.

If we create things out of place, the above generic error gets displayed.

a.vb
Public Class zzz
Shared Sub Main()
dim a as yyy
a.i = 10
a.k.j = 20
System.Console.Writeline(a.k.j + a.i)
End Sub
End Class
public structure yyy
dim i as integer
dim k as aaa
structure aaa
dim j as integer
end structure
end structure

Output
30

In the structure yyy, we have a variable i of type integer, followed by a variable k of type aaa.
The type aaa is a structure that is created within yyy. This is legally allowed in .Net.

The unfortunate part is that the outer structure cannot directly access the members of the
inner structure. The outer structure has to first create an object of type aaa, in this case k, and
then access the structure members individually, using the dot notation.

a.vb
Public Class zzz
Shared Sub Main()
dim a as yyy
a.i = 10
a.k = new yyy.aaa
a.k.j = 20
System.Console.Writeline(a.k.j + a.i)
End Sub
End Class
public structure yyy
dim i as integer
dim k as aaa
class aaa
public dim j as integer
end class
end structure

Output
30
Similar to a structure, a class too can be created inside a structure, but with a few
modifications and additions. That is, the instance members of the class, which are 'private' by
default, have to be modified to 'public'.

This is the singular change required in the structure yyy. The major change occurs in the sub
main, where an instance of class aaa has to be created. If we do not create it, an exception will
be thrown at run time. The name of the class cannot be referred to as 'aaa'; instead, it has to
be referred to as yyy.aaa, since it resides in the structure.

Thus, a new instance of the object aaa is created by using yyy.aaa with the 'new' keyword. The
object k is then initialized to this value. The statement of 'dim k as new aaa' is not allowed for
defining k because, the 'new' keyword is not allowed within structures. The class aaa can also
be referred to as an inner class or a nested class.

A structure with the access modifier of 'friend' can be used from anywhere in the same
assembly. Every member of the structure must be explicitly declared. Thus, all variables must
either use DIM or one of the access modifiers of 'public', 'private' or 'friend'. The 'protected'
modifier is not allowed, as explained earlier.

A structure could have properties, constants and procedures, but the absence of a non-shared
variable will compel the compiler to show an error. There are no scoping rules for a structure.
Thus, all the members are visible to each other. A structure,as well as a class, facilitates the
construction of user-defined data structures.

Enumerators

a.vb
Public Class zzz
Shared Sub Main()
dim a as aaa
System.Console.WriteLine("{0} {1}",a.a1,CInt(a.a2))
End Sub
End Class
public enum aaa
a1
a2
end enum

Output
a1 1

The above example explains the concept of an enum, which is a short form for 'enumerator'. An
enum is very similar to a structure or a class, since it merely creates a type. An enum type
called aaa is created, containing two members of a1 and a2.
To display the member, the dot notation is used as follows: a.a1. This is similar to the notation
used in structures. In the WriteLine function, if we use the name a.a2, it returns the value of
'a2'. However, if you use the CInt function with the name, it returns the number 1 instead. We
will cater to conversion functions such as CInt in greater detail later.

The CInt function converts the parameter given to it into a number or integer. In an enum type,
the first member is assigned a value of 0, the second one is assigned a value of 1, and so on.
An enum object allows the use of the member name instead of a number, thus making the
programs more readable.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
public enum aaa
end enum

Error
c:\il\a.vb(5) : error BC30280: Enum 'aaa' must contain at least one member.

The enum follows the rules of a structure, wherein, it requires at least one member.

a.vb
Public Class zzz
Shared Sub Main()
dim a as new aaa
System.Console.WriteLine("{0} {1} {2}",CInt(a.red),CInt(a.blue),CInt(a.green))
End Sub
End Class
public enum aaa
red
blue = 100
green
end enum

Output
0 100 101

An enum object, such as a structure, does not need the 'new' keyword to instantiate it.
However, if we use it, no error is generated. Enums are used to document numbers. For
example, it is much more intuitive to use a.green, instead of a number.

The default values assigned to each and every member of the enum can be changed easily.
Thus, the member red takes the default of 0, whereas the value of member blue is changed to
100. This results in green, which is the next member, being assigned a value of 101.

The Enum type inherits from the System.Enum class, and is normally used to represent a
small set of numeric values using names.

The default data type for the numbers is 'integer', which can be changed by using the 'as'
clause. For example, "public enum aaa as long" will change the default type to 'long'.
Alternatively, the type can be changed to 'byte short' etc.
These types differ from each other in terms of the range of numbers that they can store. The
scope of the members is restricted to the enum definition. Outside the enum, every member
has to be qualified by the name of the enum object.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
enum aaa as Double
green
end enum

Error
c:\il\a.vb(5) : error BC30650: Enums must be declared as 'Byte', 'Short', 'Integer' or 'Long'.

The above error message clearly identifies the four data types that an enum type can possess.
Types such as Double are strictly prohibited.

a.vb
Public Class zzz
Shared Sub Main()
System.Console.WriteLine("{0} {1} {2}",CInt(aaa.red),CInt(aaa.blue),CInt(aaa.green))
End Sub
End Class
public enum aaa as long
red
blue = 100
green
end enum

Output
0 100 101

There is no need to create an object of type enum, in order to use an enum member. This point
has been demonstrated in the program above. The name of the enum aaa is sufficient to access
the members of red, green and blue. This is the preferred method while using enums.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
enum aaa as long
green
public red
end enum

Error
c:\il\a.vb(7) : error BC30619: Statement cannot appear within an Enum body. End of Enum
assumed.
An enum member cannot have any access modifier associated with it. The default access
assigned to each of the members is 'public'. The error message is generic in the sense that,
whenever we place something inappropriate in an enum, the above error message is displayed.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
enum aaa as byte
green
red = 1000
end enum

Error
c:\il\a.vb(7) : error BC30439: Constant expression not representable in type 'Byte'.

In the above program, the enum type is declared to contain Bytes. Since a byte value cannot
exceed beyond 255, the above error gets reported. The value of 1000 exceeds the range of the
byte type, thus causing the error.

a.vb
Public Class zzz
shared public dim i as integer
Shared Sub Main()
End Sub
End Class
enum aaa as byte
green
red = zzz.i
end enum

Error
c:\il\a.vb(8) : error BC30059: Constant expression is required.

The value of an enum has to be a constant, which means that the value must be available at
compile time. Initializing the member red to zzz.i would have worked fine if 'i' had a constant
value. But since 'i' is a static variable in the class zzz, its value is likely to be changed in the
constructor. This ambiguity is not acceptable. Hence, the error message is generated. The
enum members have to be assigned values that are constants.

a.vb
Public Class zzz
Shared Sub Main()
System.Console.WriteLine("{0} {1} {2}" , CInt(aaa.green),CInt(aaa.red),CInt(aaa.blue))
End Sub
End Class
enum aaa as long
green = 1
red = green
blue = red
end enum

Output
111

Members of the enum can be assigned the same values. In the program, the member green has
been given a value of 1. Then, the member red is equated to green, resulting in the value of 1
being assigned to red also. Finally, blue is assigned the value of red, which again is 1.
Therefore, finally, the value contained in all the three members is 1. The example also proves
the fact that one member in an enum can be initialized to another member.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
enum aaa as long
green = red
red = green
end enum

Error
c:\il\a.vb(7) : error BC30500: Constant 'green' cannot depend on its own value.

We may slump at the wheel, but the compiler does not. The above example is a classical case of
a 'circular reference'. The member green depends upon the member red, which in turn depends
on the member green. The Visual Basic.Net compiler detects such circular references and gives
the above error.

enum aaa as long


green = red
red
end enum

The above example generates the same error because the member green depends upon the
member red explicitly, while the member red internally depends upon the member green, since
it simply increments the value of member green by 1.

a.vb
Public Class zzz
Shared Sub Main()
dim a as integer = 1
if a = aaa.green then
System.Console.WriteLine("hi")
end if
End Sub
End Class
enum aaa
green = 1
end enum

Output
hi

Even though an enum is a type by itself, we are allowed to compare an enum with a number.
The 'if' statement results in true, because the value of green is 1, and 'a' is declared with a
value of 1. As we mentioned earlier, enums are used, to avoid using numbers. An enum is a
symbolic name for a set of values. It does add any overhead to the program, since the compiler
replaces the enum values with the actual values at compile time.

The .Net world has plenty of enums, which are implemented in various programs.

a.vb
Public Class zzz
enum aaa
green
end enum
Shared Sub Main()
End Sub
End Class

An enum data structure can be declared in a source file, or in a module, or in a structure, or


even in a class, as the above example shows. However, it cannot be placed in a procedure. An
enum data type has all the rights of a data type created by a class, and hence, it can be used
as a type in a function parameter, as well as a parameter to a constructor.

Class Statements

a.vb
dim a as integer
public class zzz
shared sub Main()
end sub
end class

Error
c:\il\a.vb(1) : error BC30001: Statement is not valid in a namespace.

Our primary focus is on the class, because everything in Visual Basic.Net must be placed in a
class. Variables, subs etc cannot be created outside a class. The above error will be generated
whenever an entity is created outside a class. There are very few entities that can be created
outside a class; however, they still have to be placed within a namespace.

Let us now look at the modifiers that can be used with the DIM statement when creating an
object or a variable.

a.vb
public class zzz
dim readonly a as integer
shared sub Main()
dim z as zzz = new zzz
z.abc
end sub
sub abc
a = 10
end sub
end class

Error
C:\il\a.vb(8) : error BC30064: 'ReadOnly' variable cannot be the target of an assignment.
The ReadOnly modifier gives only read permission to the variable; the variable cannot be
written to. Thus, attempting to change the value of the variable in the sub abc would be asking
for trouble.

a.vb
public class zzz
dim readonly a as integer = 20
shared sub Main()
dim z as zzz = new zzz
z.abc
end sub
sub abc
System.Console.WriteLine(a)
end sub
end class

Output
20

The above example depicts the correct approach while using readonly variables. A ReadOnly
variable can be initialized at the time of creation. Thereafter, no changes to it are permitted. Its
value can be used, but not changed.

However, there is one exception to this rule. The value of a readonly variable can be changed in
the constructor or the sub 'new', as shown below. Add the following lines of code before the end
class. This results in the value of 'a' being displayed as 10, and not 20.

sub new
a = 10
end sub

The DIM statement can be used at the module, class, structure, procedure or block level. In
other words, other than certain exceptions, like we have shown above, a DIM statement can be
used anywhere in the program.

Similar to the DIM statement, the access modifiers too can be used anywhere in the code.
a.vb
Public Class zzz
Shared Sub Main()
dim a as new zzz
dim i as integer = 1
while ( i <= 5 )
a.abc
i=i+1
end while
End Sub
sub abc
static dim j as integer = 10
j=j+1
System.Console.Write("{0} ", j)
end sub
End Class

Output
11 12 13 14 15

Removal of the keyword 'static' from the DIM statement in the above program generates the
following output:

Output
11 11 11 11 11

Earlier, we had briefly mentioned that a variable created in a sub remains alive as long as the
sub is alive. The moment the sub finishes execution, the variables created in it become
inaccessible, since they cease to exist.

The sub abc is called 5 times, using the loop construct. Within the sub, a variable j is created
using the modifier of 'static', thus making 'j' a static variable.

A static variable acts as an instance variable to the extent that, it retains its value across
function invocations. However, it cannot be accessed outside the sub that it is created in.
Thus, the variable j is initialized to 10 only when the DIM statement is encountered for the first
time.
Had it not been a static variable, the sub would have had to initialize the variable j to 10 every
time. However, since it has been declared as static, succeeding calls to the function ignore the
DIM statement. This is why it is created only once, and thereafter, it retains its value.

a.vb
Public Class zzz
static a as integer
Shared Sub Main()
End Sub
End Class

Error
c:\il\a.vb(2) : error BC30235: 'Static' is not valid on a member variable declaration.

Static variables can be placed in a shared or non-shared method, but they cannot be created
outside a procedure, at the class or module level. This error is obvious, since static variables
are instance variables, with local access.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
static sub abc
end sub
End Class

Error
c:\il\a.vb(4) : error BC30810: Methods cannot be declared 'Static'.

Methods obviously cannot be declared static, since the concept of static applies to variables
only.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
public structure aaa
dim j as integer
sub abc
static i as integer
end sub
end structure

Error
c:\il\a.vb(8) : error BC31400: Local variables within methods of structures cannot be declared
'Static'.

An additional restriction on static variables is that, they cannot be created within procedures
in a structure. Methods are not preferred entities in a structure.

a.vb
Public Class zzz
Shared Sub Main()
static i as integer
static i as integer
End Sub
End Class

Error
c:\il\a.vb(4) : error BC31401: Static local variable 'i' is already declared.

We are obviously are not allowed to create entities of any type having the same name. This rule
applies to static variables also. The simple reason for this is that the variables bearing the
same names would confuse the Visual Basic.Net compiler, since it would not be able to identify
the variable that it has to use.

a.vb
Public Class zzz
Shared Sub Main()
dim i as integer
dim i as integer
End Sub
End Class

Error
c:\il\a.vb(4) : error BC30288: Local variable 'i' is already declared in the current block.

The rule pertaining to duplicate names applies to local and instance variables also, but the
error number and message that is generated are very different from those generated for
duplicate static variables.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
public structure zzz
dim i as integer
end structure
Error
c:\il\a.vb(1) : error BC30179: class 'zzz' and structure 'zzz' conflict in namespace '<Default>'.

Structures and classes too cannot bear identical names, as they both share the same
namespace. Here, the meaning of the word namespace is very different from what we have
learnt earlier.

Each time a type is created, Visual Basic.Net stores the type in a specific area in memory. As it
stores the types created by a class and a structure in the same area of memory, the above error
gets reported.

However, a class and a variable in the class can both have the same name, since the class
names and the members of a class are stored in different namespaces. On the other hand,
within a class, the fields and methods cannot have identical names, since all the members of
the class are stored in the same namespace.

a.vb
Public Class zzz
Shared Sub Main()
shared dim zzz as zzz
End Sub
End Class

Error
c:\il\a.vb(3) : error BC30247: 'Shared' is not valid on a local variable declaration.

Coming back to the concept of shared variables, since they belong to a class and not a
function, they cannot be created in a function.

Local variables are not visible outside of a function, and hence, the rest of the class members
cannot access local variables.

The above error message is generic, and it shows up whenever a modifier is applied to a local
variable that is not applicable. There are certain combinations like 'static' and 'shared', which
obviously are incompatible with each other, since 'static' is applied to local variables, whereas
'shared' is applied to instance variables.

a.vb
Public Class zzz
dim i as integer
Shared Sub Main()
dim a as new zzz
a.i = 100
a.abc
System.Console.WriteLine("After abc {0}",a.i)
a.pqr
System.Console.WriteLine("After pqr {0}",a.i)
End Sub
sub abc
dim i as integer
i = 10
System.Console.WriteLine("abc {0}",i)
end sub
sub pqr
abc
System.Console.WriteLine("pqr {0}",i)
i = 20
end sub
End Class

Output
abc 10
After abc 100
abc 10
pqr 100
After pqr 20

The above example showcases a new concept called 'shadowing' a variable. An integer variable i
is created, with its value set to 100. Then, the sub abc is called. In the sub, one more variable
named i is created with the value of 10. When the value is displayed in the sub, it shows 10, as
expected.

On quitting the sub abc, in order to display the value of i in sub main, the instance 'a' is used
as a prefix, since the sub is 'shared'. The value of i still remains as 100. This goes to prove that
the two variables named i are different. This concept is termed as shadowing, where the outer
variable i, or the instance variable i gets shadowed by the inner variable i, since they both have
same names.

Then, the sub pqr is called, which calls the sub abc again. As before, the sub abc recreates the
variable i and sets it to a value of 10. Thus, the value of i in abc is shown as 10. However, when
we re-display the value of i in sub pqr, before changing its value to 20, the value shown is 100.

After assigning a new value of 20, the sub pqr quits out to the WriteLine statement in the
shared sub Main. The value displayed is 20. This proves that the i in sub pqr is the instance
variable, whereas, the i in sub abc is a local variable.

a.vb
Public Class zzz
dim i as integer
Shared Sub Main()
End Sub
sub abc
i = 10
dim i as integer
end sub
End Class

Error
c:\il\a.vb(6) : error BC32000: Local variable 'i' cannot be referred to before it is declared.

Even though there exists an instance variable i, the statement of i=10 in the sub abc gives an
error, saying that i has not been declared. This occurs in spite of following up the statement
with the DIM statement. On placing the DIM statement in comments, the error vanishes, since
instance variables can be easily accessed in functions.

The error occurs because till it reaches the DIM statement, the compiler believes that the
variable used is an instance variable. However, after recognizing the variable as a local
variable, on encountering the DIM statement, it ignores the instance variable. It also flags an
error on the initialization statement, since the variable has been created after it. If you move
the DIM statement of the sub above the initialization, everything works well.

Thus, shadowing hides the variable with the same name, and prevents the shadowed variable
from being referenced. There are two ways that we can shadow a variable:
• The first method is the one we have seen above, where we shadow using the concept of
'scoping'. The variable with the narrower scope wins.
• The second and more important way of shadowing is using the 'shadows' keyword.

a.vb
Public Class zzz
Shared Sub Main()
dim a as yyy
dim b as xxx
a = new xxx
b = new xxx
System.Console.WriteLine("{0} {1}",a.i,b.i)
End Sub
End Class
public class yyy
public dim i as integer = 10
end class
public class xxx
inherits yyy
public dim i as string = "hi"
end class

Warning
c:\il\a.vb(15) : warning BC40004: variable 'i' conflicts with variable 'i' in the base class 'yyy' and
so should be declared 'Shadows'.

Output
10 hi

In the above example, the class yyy has a member i of type integer. The class xxx inherits from
the class yyy and creates a member i of type string. At this stage, the compiler gives a warning,
and not an error.

The compiler very decently points out the fact that both variables are named i. Therefore, the
one located in the derived class should use the modifier of 'shadows'.

By adding the modifier 'shadows' to the variable i in the DIM statement, the warning
disappears.

public class xxx


inherits yyy
shadows public dim i as integer = 100
end class

The same warning would also be issued, if the data types of the variables are identical.

public class xxx


inherits yyy
sub i
end sub
end class

Error
c:\il\a.vb(10) : warning BC40004: sub 'i' conflicts with variable 'i' in the base class 'yyy' and so
should be declared 'Shadows'.

The compiler dispatches a similar warning when the class xxx has a sub called i, instead of
variable called i. It is advisable not to create any entity in the derived class having an identical
name to an entity in the base class.

Thus, the 'shadows' modifier serves the purpose of informing the compiler about an identically
named programming element in the base class. The 'shadows' keyword also prevents the
derived class from accessing the base class member.

The 'shadows' modifier can only be used at the module, namespace or file level. This implies
that we can use it in a source file, module, class or structure, but not in a procedure. Since
shadows cannot be used inside a procedure, the modifiers of 'static' and 'shadows' cannot be
used at the same time.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
public class yyy
shadows public dim i as integer = 10
end class

Since shadows only emits a warning, even though it hides something from the base class, in
the above case, we shadow the variable i in the class yyy. There is no variable i in the base
class that the class yyy derives from. Hence, the compiler does not generate any alarm.

We are allowed to redefine any base class element that we like. There is a major difference
between 'shadowing' and 'overriding', both of which apply to derived classes.

When we override a procedure, we make sure that the new procedure gets called, but the
procedure of calling it remains the same.

In shadowing, we protect the base class from being modified, by having a similar member in
the base class later. The redefined element and defining element can be of any type, but in
'overriding', the redefined element must be a procedure and the refining element must be a
procedure that has the same calling sequence.

The signature consists of the element type (i.e. function, sub or property, name, argument list)
and the return type.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
class yyy
Inherits xxx
Overrides sub abc( i as integer )
end sub
end class
class xxx
Overridable sub abc
end sub
end class

Error
c:\il\a.vb(7) : error BC30284: sub 'abc' cannot be declared 'Overrides' because it does not
override a sub in a base class.

Warning
c:\il\a.vb(7) : warning BC40003: sub 'abc' shadows an overloadable member declared in the base
class 'xxx'. If you want to overload the base method, this method must be declared 'Overloads'.

There is one error and one warning above. The error is clear, in that, we can only override a
sub abc if there is a sub abc with the same integer as a parameter. Here, this is not the case,
and hence, the error is generated.

This error occurs because, in the base class xxx, the sub abc does not have any parameters.
Thus, there is nothing to override. The warning is not important, as the keyword 'overloads' is
optional, and it is a hint to the Visual Basic.Net compiler that we are overloading a procedure.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
class yyy
Inherits xxx
Overrides function abc as integer
end function
end class
class xxx
Overridable sub abc
end sub
end class

Error
c:\il\a.vb(7) : error BC30437: 'Public Overrides Function abc() As Integer' cannot override 'Public
Overridable Sub abc()' because they differ by their return types.

In the class yyy, we now change the sub abc to a function abc. This gives us an error because
as explained above, we are allowed to change the types when we shadow, but not when we
override.
The keyword shadows is recommended in the derived class, which means that it is optional. If
the Overrides keyword is not present, the default is shadows. In the Overriding case, the
keyword 'overridable' is mandatory in the base class, and the keyword 'overrides' is a must in
the derived class.

a.vb
Public Class zzz
Shared Sub Main()
dim a as yyy
dim b as xxx
dim c as vvv
a = new vvv
b = new vvv
b.abc
c = new vvv
c.abc
System.Console.WriteLine("{0} {1} {2}",a.i,b.i,c.i)
End Sub
End Class
public class yyy
public dim i as integer = 10
end class
public class xxx
inherits yyy
shadows private dim i as string = "hi"
sub abc
System.Console.WriteLine("abc {0} ",i)
end sub
end class
public class vvv
inherits xxx
end class

Output
abc hi
abc hi
10 10 10

We start with a class yyy, which has a public integer variable i. The important point is the use
of the modifier 'public'. Then, the class xxx that derives from class yyy also has an integer i
that is 'private'. This means that only the members of the class yyy, like the sub abc, are
allowed to access the integer i.

This integer i is associated with the 'shadows' keyword, to get rid of the warning message that
gets displayed. Finally, we have a class vvv that derives from the class yyy. It has no members.
We create three objects a, b and c of types yyy, xxx and vvv, respectively. Then, we initialize
them to a derived vvv object.

We call the sub abc only from the b and c objects, since the class yyy has the sub abc. In this
sub, the integer i is the string type created as a private variable. When we display the member i
from the yyy object 'a', the integer i gets displayed.

When we display the member i from the xxx class, since the member i is private, we are only
allowed to reference the member i from the class yyy. The 'private' modifier overshadows the
'shadows' keyword. In the class vvv, the i that is visible is the integer i from the class yyy.

Thus, when we shadow an element, it is also shadowed in the derived class. If we remove the
private keyword, and make it public in class xxx, the class vvv will see the value of i as "hi" and
not as 10. During overriding, the overriding element is inherited by further derived classes, and
the overridden element is overridden yet again.

a.vb
Public Class zzz
Shared Sub Main()
dim b as xxx
b = new xxx
b.abc
End Sub
End Class
public class yyy
public dim i as integer = 10
end class
public class xxx
inherits yyy
shadows public dim i as string = "hi"
sub abc
System.Console.WriteLine("{0} {1}",Me.i,MyBase.i)
end sub
end class

Output
hi 10

Nothing prevents us from accessing the shadowed member, by using the MyBase keyword. It
allows us to access members from the base class, whether they are shadowed or not. The Me
keyword permits us to access the current class, and as mentioned earlier, it is optional.

Thus, in the world of shadowing, we are re-declaring a member. When we shadow, as seen in
the above example, we are not removing a member. Instead, we are simply hiding it.

a.vb
class zzz
shared Sub Main()
Dim a As xxx = New xxx()
a.pqr(1,2)
a.pqr()
a.pqr(10)
End Sub
End class
public Class yyy
Sub pqr()
End Sub
Sub pqr(i As Integer)
End Sub
End Class
public Class xxx
Inherits yyy
Shadows Sub pqr(i As Integer , j as integer)
End Sub
End Class
Error
c:\il\a.vb(5) : error BC30455: Argument not specified for parameter 'i' of 'Public Shadows Sub
pqr(i As Integer, j As Integer)'.

The above example generates four errors. We start by declaring the class yyy to have two subs,
both named pqr, with zero and one parameter, respectively. Then, we come to class xxx, which
inherits from class yyy and brings in one sub abc that accepts two parameters.
The shadows keyword ensures that the subs created in the base class are hidden. This hides
the subs abc, having zero and one parameter. Thus, the compiler sees only one sub in the class
yyy, which has two parameters.

In the sub main, the call of the sub abc with two parameters gives no errors, but the other two
calls of the sub abc are erroneous, due to the fact that the shadows keyword conceals the other
two subs.

If we remove the shadows keyword, we do not get any error, since the class xxx now contains
three abc subs, i.e. two from the class yyy, and one from the class xxx. If we add the keyword
'overloads', no error is generated, even though this sub abc is not overloaded with two
parameters.

a.vb
class zzz
shared Sub Main()
Dim a As xxx = New xxx()
a.pqr(10)
a.pqr()
End Sub
End class
public Class yyy
Sub pqr()
End Sub
Sub pqr(i As Integer)
End Sub
End Class
public Class xxx
Inherits yyy
Overloads Sub pqr(i As Integer )
End Sub
End Class

The above example reiterates what we have said above. The class yyy now has two subs abc.
The first is the sub abc with no parameters, from the class yyy, and the other is from the class
xxx with one parameter. This overrides the one from the class yyy. If we change the 'overloads'
keyword to 'shadows', we get the error given below:

Error
c:\il\a.vb(5) : error BC30455: Argument not specified for parameter 'i' of 'Public Shadows Sub
pqr(i As Integer)'.

This error occurs since the 'shadows' keyword hides the two abc subs, and we are left with only
one abc sub with an integer as a parameter. As mentioned earlier, shadows is the default.

a.vb
class zzz
shared Sub Main()
End Sub
End class
public Class yyy
public dim i as integer
End Class
public Class xxx
inherits yyy
shadows public dim i as integer
End Class
public Class vvv
inherits xxx
public dim i as integer
End Class

Error
c:\il\a.vb(14) : warning BC40004: variable 'i' conflicts with variable 'i' in the base class 'xxx' and
so should be declared 'Shadows'.

We have three classes yyy that have one integer i, the class xxx that derives from class yyy and
has a integer i, and finally, the class vvv, which in turn derives from the class xxx, It also has
one integer i.
The class xxx uses the 'shadows' keyword on the definition of the variable i, to stop the
compiler from emitting the warning. In the class vvv, while creating the variable i, we have used
the 'shadow' keyword. Thus, the compiler gives us a warning, since the 'shadow' keyword does
not pass on to the derived classes by default. We have to explicitly mention it, in order to avoid
the warning. No error is generated.

a.vb
class zzz
shared Sub Main()
End Sub
End class
public Class yyy
shadows overloads sub abc
end sub
End Class

Error
c:\il\a.vb(6) : error BC31408: 'Overloads' and 'Shadows' cannot be combined.

We cannot combine 'shadow' and 'overloads', as they are mutually exclusive. The shadow
concept hides things, whereas, overloads allows us to us override something. If you remember,
earlier we had encountered the error message that overrides and shadows cannot be combined.
Thus, we once again have a generic error message.

a.vb
class zzz
shared Sub Main()
Dim a As aaa = new ddd
Dim b As bbb = new ddd
Dim c As ccc = new ddd
Dim d As ddd = new ddd
a.abc()
b.abc()
c.abc()
d.abc()
End Sub
End class
Class aaa
Public Overridable Sub abc()
System.Console.WriteLine("aaa")
End Sub
End Class
Class bbb
Inherits aaa
Public Overrides Sub abc()
System.Console.WriteLine("bbb")
End Sub
End Class
Class ccc
Inherits bbb
Public Overrides Sub abc()
System.Console.WriteLine("ccc")
End Sub
End Class
Class ddd
Inherits ccc
Public Overrides Sub abc()
System.Console.WriteLine("ddd")
End Sub
End Class

Output
ddd
ddd
ddd
ddd

We start by having four classes aaa, bbb, ccc and ddd, which have one sub abc. Then, we
create four objects a,b, c and d of types aaa, bbb, ccc and ddd, respectively. Then, we initialize
all of them to an instance of class ddd.

If the compile type and run time type are the same, it is very easy to figure out as to which
function has been called. The dilemma is caused when the compile time data type is different
from the run time data type, as in the above example.

If we call the sub abc using any of the objects, let us try to decipher as to which sub abc would
be called. The sub abc in class aaa is overridable, which means that only the derived classes
have permission to override this sub by the class aaa. The derived classes bbb, ccc and ddd
override this sub. Hence, in all the cases, the sub abc will be called from class ddd. This was
explained earlier, but not using four classes, as we have done in the above example.

It is mandatory for us to specify the overrides for both the classes bbb and ccc, or else, an error
will result. This is because, in the class aaa, we cannot specify the keyword overrides, as we
have already specified the keyword overridable.

Class aaa
Public Overridable Overrides Sub abc()
System.Console.WriteLine("aaa")
End Sub
End Class

Error
c:\il\a.vb(14) : error BC30730: Methods declared 'Overrides' cannot be declared 'Overridable'
because they are implicitly overridable.

Just to prove our point, we added the keyword 'overrides' to the class aaa in sub abc, and
obviously obtained the above error. The error boldly proclaims that when we make a sub
overridable, we are also overriding it by default. Then, we remove the 'overrides' keyword from
the sub abc class aaa.

It is not mandatory for the sub abc in class ddd to have the overrides keyword, as we may not
want to override the sub abc from the base class. Technically speaking, the class aaa uses the
'overridable' keyword, and the classes bbb, ccc and ddd do not have to contain the keyword
'overrides'.

However, if the class bbb decides to have the keyword 'overrides', the classes ccc and ddd can
ignore it. However, if the class ccc uses the 'overrides' keyword, then the class bbb has to also
contain the 'override' keyword, whereas the class ddd does not have to.

Thus, if the derived class uses the 'override' keyword, all the base classes must also use the
'override' keyword or the 'overridable' keyword. We shall explain this shortly.

a.vb
Class ccc
Inherits bbb
public overridable Sub abc()
System.Console.WriteLine("ccc")
End Sub
End Class

Warning
c:\il\a.vb(26) : warning BC40005: sub 'abc' shadows an overridable method in a base class. To
override the base method, this method must be declared 'Overrides'.

Output
bbb
bbb
ddd
ddd

The overrides keyword for the sub abc in class ccc has been replaced by the overridable
keyword. By doing this, we not only receive a warning from the compiler, but the output also
changes dramatically. To get rid of the warning, add the shadows keyword to the sub abc in
class ccc as follows:
public shadows overridable Sub abc()
The 'shadows' keyword normally does not display any errors, but only generates pesky
warnings. Let us understand the output of the programs above. The first two objects a and b
call the sub abc from the class bbb, and in the last two cases, the c and d objects call the sub
abc from the class ddd.

So far, we have learnt that we can shadow inherited methods or the methods from a base class.
Thus, in the above case, we have four classes that have the same method signature for the
method abc. The Visual Basic.Net complier has no problems here, as only one abc sub will be
visible, which is the last derived one.
The class bbb overrides the sub abc from the class aaa. The class ccc creates a new sub abc,
which has nothing to do with the sub abc from the class bbb. Thus, for the classes aaa and
bbb, there will be one sub, and for classes ccc and ddd, there will be a different sub.

The overridable keyword thus behaves like the Great Wall of China, by giving two different abc
subs in the classes aaa and ccc. The sub in class aaa class is overridden by the one in class
bbb, and the sub in class ccc is overridden by the one in class ddd. It is not possible for the
class ddd to override the sub of class aaa, since its scope is limited to class ccc only.

a.vb
class zzz
shared Sub Main()
End Sub
End class
Class aaa
Public Sub abc()
End Sub
private Sub pqr()
End Sub
End Class
Class bbb
Inherits aaa
Public Sub abc()
End Sub
public Sub pqr()
End Sub
End Class

Warning
c:\il\a.vb(13) : warning BC40004: sub 'abc' conflicts with sub 'abc' in the base class 'aaa' and so
should be declared 'Shadows'.

For the sake of revision, whenever two subs are created with the same name in the base and
derived classes, the shadows keyword must be implemented. Not doing so, will result in a
pesky warning No.BC40004.

This is proved by the warning issued by the above example, where the base class aaa has a sub
abc, and the derived class bbb too has the same sub abc.

The sub pqr is also present in the two classes, but there is no error or warning issued for it, as
the sub pqr is 'private' in the base class, and thus, not visible in the derived class bbb. The
class bbb remains unaware of the sub pqr in class aaa, and hence, no warnings get generated.

Had we changed the private access modifier to protected, a warning would have been issued,
since derived classes cannot access protected members.

Shadowing an inherited name does not result in an error, but only in a warning. This is
because, if the base class aaa does not have a sub pqr, and if one is introduced later, derived
classes like bbb that have a pqr sub should not give an error. A warning is OK.

Thus, whenever a method is added to the base class that was not present earlier, but exists in
the derived classes, no errors result. A warning is issued as the method in the base class can
either be removed by the shadows keyword or the overloads keyword, but not by both.
a.vb
class zzz
shared Sub Main()
dim a as new ccc
a.abc
a.pqr
End Sub
End class
Class aaa
Public Sub abc()
System.Console.WriteLine("aaa abc")
End Sub
End Class
Class bbb
Inherits aaa
Private Shadows Sub abc()
System.Console.WriteLine("bbb abc")
End Sub
public sub xyz
abc
end sub
end Class
Class ccc
Inherits bbb
Sub pqr()
abc
MyBase.abc
xyz
End Sub
End Class

Output
aaa abc
aaa abc
aaa abc
bbb abc

The class aaa having sub abc, is the base class for class bbb, which too introduces a sub by
the same name. The private keyword is used in the class bbb on the sub abc. Thus, the sub
abc remains hidden from any class derived from it, such as class ccc.

Now, when we call the sub abc from the object 'a' of type ccc, since the class ccc does not have
a sub abc, the system moves on to class bbb. Since it does not see any sub called abc, as it is
private, the sub abc from class aaa is called instead. Then, the sub pqr is called from the class
ccc, where the subs of abc, MyBase.abc and xyz are called.

The first call of abc will be attended by the sub abc from class aaa, for the reasons mentioned
earlier. The second call with the identifier of MyBase, will call the sub abc from the base class
bbb. However, as it does not have the sub abc due to it being private, the sub will be called
from the class aaa, like before.
Finally, the sub xyz will be called from the class bbb. The sub xyz simply calls the sub abc
again. As access modifiers are not applicable to members of the same class, the private sub abc
in the class bbb gets executed.

The things we do with the class statement!

a.vb
class zzz
inherits System.Array
shared Sub Main()
End sub
End class

Error
c:\il\a.vb(2) : error BC30015: Inheriting from 'System.Array' is not valid.

There are four classes that a class cannot derive from. These classes are System.Array,
System.Delegate, System.Enum and System.ValueType. There is no reason specified for this
constraint.

a.vb
class zzz
inherits zzz
shared Sub Main()
End sub
End class

Error
c:\il\a.vb(2) : error BC30256: Class 'zzz' cannot inherit from itself:

A class can inherit from other classes, but not from itself. There is a separate error message
provided for this inanity.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
End Class
interface bbb
inherits bbb
end interface

Error
c:\il\a.vb(5) : error BC30295: Interface 'bbb' cannot inherit from itself:

Similarly, an interface is also not allowed to inherit from itself.

a.vb
class zzz
shared Sub Main()
dim a as aaa
a = new aaa
End Sub
End class
mustinherit public class aaa
end class

Error
c:\il\a.vb(4) : error BC30569: 'New' cannot be used on a class that is declared 'MustInherit'.
The mustinherit keyword on a class prevents creation of an instance of the class using the
'new' keyword. The class can be used as a type in the DIM statement, but it is not allowed
access by any instance members.

a.vb
class zzz
shared Sub Main()
aaa.abc
End Sub
End class
mustinherit public class aaa
shared sub abc
System.Console.WriteLine("abc")
end sub
end class

Output
abc

The above example calls the shared sub from a mustinherit class. This does not generate any
errors, whatsoever. Thus, a mustinherit keyword, known as 'abstract' in other languages,
prevents its use directly, but allows it in derived classes.

a.vb
class zzz
shared Sub Main()
dim a as aaa
dim b as bbb
a = new bbb
b = new bbb
a.abc
b.abc
End Sub
End class
mustinherit public class aaa
sub abc
System.Console.WriteLine("abc")
end sub
end class
public class bbb
inherits aaa
end class

Output
abc
abc

The class aaa uses the mustinherit keyword, and simultaneously creates a sub abc. The class
bbb derives from class aaa. It contains no other code. Two variables, a and b of type class aaa
and class bbb, respectively, are created in sub main. Both are instances of type bbb.
By making both the variables as bbb objects, the sub abc present in the class aaa can be called
by both the objects. Thus, the mustinherits class must not be used directly. In addition to this,
the sub abc can be created in the class bbb, since the rules of shadowing apply here too.

The mustinherit keyword on a class denotes the fact that the class is an incomplete type, and
should not be used directly. Thus, there are differences in the usage of mustinherit classes and
normal classes. The major difference is illustrated in the next program.

a.vb
class zzz
shared Sub Main()
End Sub
End class
public class aaa
mustoverride sub abc
end class

Error
c:\il\a.vb(5) : error BC31411: 'aaa' must be declared 'MustInherit' because it contains methods
declared 'MustOverride'.

In a method, only the signature suffices when the mustoverride keyword is attached to it.
Thus, there is no need of an 'end sub' or any code. Howevrer, this keyword can only be used on
a class that is stamped as mustinherit. Since this clause was not implemented, the compiler
gives the above error message.

a.vb
class zzz
shared Sub Main()
End Sub
End class
mustinherit public class aaa
mustoverride sub abc
public sub pqr
end sub
end class
public class bbb
inherits aaa
end class

Error
c:\il\a.vb(10) : error BC30610: Class 'bbb' must either be declared 'MustInherit' or override the
following inherited 'MustOverride' member(s): Public MustOverride Sub abc().

After inserting the keyword of mustinherit on the class aaa, the error vanishes. Also, creating a
sub pqr with code gives no error. The problem arises only when class bbb is derived from class
aaa. The compiler points out the missing implementation of the method abc, and suggests two
options for solving it.

a.vb
class zzz
shared Sub Main()
End Sub
End class
mustinherit public class aaa
mustoverride public sub abc
public sub pqr
end sub
end class
public class bbb
inherits aaa
public overrides sub abc
end sub
end class

The keyword overrides has been added to the sub abc. The absence of this keyword would
result in an error. Thus, every incomplete method in the class aaa has to be implemented in
the class bbb with the overrides keyword. The second suggestion is implemented as follows.

a.vb
class zzz
shared Sub Main()
End Sub
End class
mustinherit public class aaa
mustoverride public sub abc
mustoverride public sub pqr
end class
mustinherit public class bbb
inherits aaa
public overrides sub abc
end sub
end class
public class ccc
inherits bbb
public overrides sub pqr
end sub
end class

In this approach, the class aaa uses the mustinherit keyword. It has two subs abc and pqr,
with only their prototypes. These obviously are marked with the keyword mustoverride. The
class bbb is incomplete, and cannot be used, as it inherits from the class aaa. It only
implements the sub abc and not the sub pqr.

Therefore, the class bbb is marked as mustinherit. The class ccc is alright, as it derives from
class bbb and implements the sub pqr also. Thus, in all, there are two alternatives, i.e. either
implement all subs present in the mustinherit base class within the derived class, or use the
mustinherit keyword.

a.vb
class zzz
shared Sub Main()
End Sub
End class
notinheritable public class aaa
end class
public class bbb
inherits aaa
end class
Error
c:\il\a.vb(8) : error BC30299: 'bbb' cannot inherit from class 'aaa' because 'aaa' is declared
'NotInheritable'.

The keyword notinheritable prevents any other class from inheriting from the class. Thus, as
the class aaa is declared notinheritable, the class bbb cannot be derived from it. However, the
class functions as a normal class.

a.vb
class zzz
shared Sub Main()
End Sub
End class
notinheritable mustinherit public class aaa
end class
public class bbb
inherits aaa
end class

Error
c:\il\a.vb(5) : error BC31408: 'MustInherit' and 'NotInheritable' cannot be combined.
The mustinherit keyword specifies that the class cannot be used directly, but only through
inheritance. The notinheritable is the reverse, i.e. it can only be instantiated and used for
inheritance. Thus, this causes the contradiction, and the obvious error results.

a.vb
class zzz
shared Sub Main()
End Sub
End class
MustInherit Class aaa
Public MustOverride Sub abc()
End Class
MustInherit Class bbb
Inherits aaa
Public Shadows Sub abc()
End Sub
End Class
Class ccc
Inherits bbb
public shadows sub abc
end sub
End Class

Error
c:\il\a.vb(10) : error BC31404: 'Public Shadows Sub abc()' cannot shadow a method declared
'MustOverride'.
c:\il\a.vb(13) : error BC30610: Class 'ccc' must either be declared 'MustInherit' or override the
following inherited 'MustOverride' member(s): Public MustOverride Sub abc().

The class aaa in the above program is tagged with mustinherit, and has a sub abc with the
mustoverride keyword. Then, the class bbb is derived from class aaa, with a shadow on the sub
abc. This results in a grave error, since the act of shadowing hides the sub abc from the class
aaa.
If the sub abc is not implemented in the class aaa, which derives from bbb, the compiler
throws an error. This is because, any other class such as ccc, which derives from class bbb,
cannot define the code for sub abc. This is because, the middle class bbb hides it from the
original base class aaa. This removes all the options of specifying a derived or descendant sub
in the class ccc. The shadows keyword is used only to get rid of the pesky warning.

a.vb
class zzz
shared Sub Main()
End Sub
End class
public Class aaa
Public overridable Sub abc()
end sub
End Class
public class bbb
inherits aaa
Public notoverridable overrides Sub abc()
end sub
end class
public class ccc
inherits bbb
Public overrides Sub abc()
end sub
end class

Error
c:\il\a.vb(16) : error BC30267: 'Public Overrides Sub abc()' cannot override 'Public Overrides
NotOverridable Sub abc()' because it is declared 'NotOverridable'.

The class aaa has a sub abc with the override modifier. Thus, it allows any derived class to
override it. Then, the class bbb derives from the class aaa, since it wants to override the sub
abc. The override modifier here is optional. In addition to this, there is a notoverridable
modifier. This does not give any errors in the class bbb.

However, the modifier flags an error in class ccc, as we have attempted to override sub abc
here. The notoverridable modifier turns down any overrides to the sub in the base class. In a
sense, what notinheritable is for a class, notoverridable is for an individual sub.
a.vb
class zzz
shared Sub Main()
End Sub
End class
public Class aaa
Public notoverridable Sub abc()
end sub
End Class

Error
c:\il\a.vb(6) : error BC31088: 'NotOverridable' cannot be specified for methods that do not
override another method.
The above error comes into play because, the notoverridable keyword can be used on a method
that actually overrides another method. Thus, two conditions need to be satisfied for employing
the notoverridable keyword:
• Firstly, in the base class, the method should say overridable.
• Secondly, in the derived class containing the keyword notoverridable, the sub must
have the overrides keyword.

a.vb
class zzz
shared Sub Main()
End Sub
End class
public Class aaa
Public mustoverride notoverridable Overridable Sub abc()
end sub
End Class

Error
c:\il\a.vb(6) : error BC30177: Only one of 'NotOverridable', 'MustOverride', or 'Overridable' can be
specified.

The three modifiers of notoverridable, mustoverride or overridable cannot be used at the same
time, as they are mutually exclusive.

a.vb
class zzz
shared Sub Main()
End Sub
End class
public Class aaa
Public shared overridable Sub abc()
end sub
End Class

Error
c:\il\a.vb(6) : error BC30501: 'Shared' cannot be combined with 'Overridable' on a method
declaration.

The shared keyword cannot be used in conjunction with the modifiers Overridable,
NotOverridable, or MustOverride.

a.vb
class zzz
shared Sub Main()
End Sub
End class
notinheritable Class aaa
Public MustOverride Sub abc()
End Class

Error
c:\il\a.vb(6) : error BC30607: 'NotInheritable' classes cannot have members declared
'MustOverride'.
There are a number of restrictions everywhere. The NotInheritable keyword cannot be used on
classes that have methods with the keyword MustOverride, since these methods have to be
implemented in derived classes. The class aaa is tagged as notinheritable. Therefore, it cannot
be used as a derived class.

The other keywords that cannot be used with notinheritable are overridable and notoverridable.
The overridable restriction is easy to understand, since a derived class can override it. Finally,
notinheritable classes do not work with derived classes.
a.vb
class zzz
shared Sub Main()
End Sub
End class
public Class aaa
Public overridable overridable Sub abc()
end sub
End Class

Error
c:\il\a.vb(6) : error BC30178: Specifier is duplicated.

Duplicates always create a problem. The same is true for keywords and modifiers. They cannot
be used twice in succession. The generic error message displayed above is shown.

a.vb
class zzz
shared Sub Main()
End Sub
overrides sub new
end sub
End class

Error
c:\il\a.vb(4) : error BC30283: 'Sub New' cannot be declared 'Overrides'.

The constructor can never be declared as overrides, since no one can override a constructor.

a.vb
class zzz
shared Sub Main()
End Sub
overridable sub new
end sub
End class

Error
c:\il\a.vb(4) : error BC30364: 'Sub New' cannot be declared 'Overridable'.

The same error is displayed as before, since a constructor cannot be declared as overridable.
The earlier error message is a specific one, whereas the one displayed here is generic, as it
applies to any sub that uses an inappropriate modifier.

Till now, we have discussed the different modifiers that can be placed on a sub or a function.
Let us now look at other aspects of a sub.
a.vb
class zzz
shared Sub Main()
dim a as new zzz
a.abc
End Sub
public sub abc
System.Console.WriteLine("vijay")
exit sub
System.Console.WriteLine("vijay")
exit sub
end sub
End class

Output
vijay

The use of exit sub in a sub assists in quitting out of the sub immediately. This behavior is
similar to the return statement. All lines of code after the exit sub are ignored. However, no
warning is issued that the above lines of code are not being executed.

a.vb
class zzz
shared Sub Main()
End Sub
public function abc
exit sub
end function
End class

Error
c:\il\a.vb(5) : error BC30065: 'Exit Sub' is not valid in a Function or Property.

The exit sub statement cannot be used in a function or a property.

a.vb
class zzz
shared Sub Main()
End Sub
public sub abc
abc = 10
end sub
End class

Error
c:\il\a.vb(5) : error BC30068: Expression is a value and therefore cannot be the target of an
assignment.

In a sub, we cannot return a value. Therefore, we cannot equate the name of the sub to a
number, like we can do in a function.

a.vb
class zzz
shared Sub Main()
End Sub
public sub abc
return 100
end sub
End class

Error
c:\il\a.vb(5) : error BC30647: 'Return' statement in a Sub or a Set cannot return a value.

In a sub, a return with a value is not permissible. The compiler catches this glitch and gives
the above error.
a.vb
class zzz
shared Sub Main()
End Sub
End class
public interface aaa
sub new
end interface

Error
c:\il\a.vb(6) : error BC30363: 'Sub New' cannot be declared in an interface.

The interface can have method prototypes, but it cannot have a sub called New, since
constructors are completely forbidden in an interface. A sub Main can exist in the interface
aaa, but no constructors are allowed.

a.vb
class zzz
shared Sub Main()
zzz.abc 10
End Sub
shared public sub abc(i as integer)
end sub
End class

Error
c:\il\a.vb(3) : error BC30800: Method arguments must be enclosed in parentheses.

A sub or a function with parameters must enclose the parameters passed to it within round
brackets. Simply specifying the parameters without the brackets, as shown above, is
unacceptable.

a.vb
option strict on
class zzz
shared Sub Main()
End Sub
public sub abc(i )
end sub
End class

Error
c:\il\a.vb(5) : error BC30209: Option Strict On requires all variable declarations to have an 'As'
clause.
a.vb
option strict on
class zzz
shared Sub Main()
End Sub
function abc
end function
End class

Error
c:\il\a.vb(5) : error BC30210: Option Strict On requires all function and property declarations to
have an 'As' clause.

The above two programs illustrate the concept of the option strict statement. So far, while
creating a variable or specifying a function return value, the 'as' modifier had been
implemented. This is optional, as the option strict statement is off, by default.

The option strict, when turned on, firmly demands that all variables have to be declared at the
time of creation. In a way, it is better to have this option on. This option also deals with type
conversions, which we shall cater to, on a different day.

option explicit vijay

Error
c:\il\a.vb(1) : error BC30640: 'Option Explicit' can be followed only by 'On' or 'Off'.

The option strict can only be followed by either of the words 'on' or 'off'. Incorporating any
words, other than the ones allowed, generates the above error message.
a.vb
class zzz
option strict vijay
shared Sub Main()
End Sub
End class

Error
c:\il\a.vb(2) : error BC30627: 'Option' statements must precede any declarations or 'Imports'
statements.

The option statements must be the first statement, and should precede all the other
statements, excluding comments. It must be the first non-comment statement, and must
precede 'using' and the namespace definitions. Thus, it must be placed at the very beginning.

option strict on
option strict off

Error
c:\il\a.vb(2) : error BC30225: 'Option Strict' statement can only appear once per file.

One last condition for the option statement is that, there can be only one such statement, since
even a single such statement proves to be quite a handful for the compiler.
 

 
10. Parameters to Procedures
All the code of Visual Basic applications is written in procedures. There are four different types
of procedures in Visual Basic:
• The first is a sub, which does not return a value.
• The second is a Function that returns a value.
• The third is the event handling code.
• The fourth is a property module that also contains code.

In VB, code cannot be written anywhere but in the above four procedures. The advantage of
using procedures is that they make applications more readable, since code that is oft repeated,
is positioned within them. It would not be wrong to say that without procedures, no application
can be written in VB.

The main logic behind having procedures is to write the code once, and then, call it several
times from different places. By writing procedures, the program can be broken down into
smaller logical units. It is always easier to debug 10 small procedures, rather than debugging
one large program.

It is on account of the presence of procedures that the functions like WriteLine are available,
and which can be used without us having to worry about how they are written.
The sub access modifier is 'public' by default, and thus, a sub can be called from anywhere in
the program.

a.vb
class zzz
shared Sub Main()
End Main
End class

Error
c:\il\a.vb(3) : error BC30678: 'End' statement not valid.

The sub Main requires an 'end sub', or else the above stated error is generated. In the world of
VB, there is no 'begin' statement, but there is a mandatory 'end' statement.

a.vb
class zzz
shared Sub Main()
call abc
End sub
shared sub abc
System.Console.WriteLine("hi")
end sub
End class

Output
hi

To call a sub or a function, the 'call' statement may be used. This, however is optional, and
hence, is not implemented in right earnest. But you are bound to encounter it sometime.

Passing Parameters

a.vb
class zzz
shared Sub Main()
End sub
sub abc( i , j as integer)
end sub
End class

Error
c:\il\a.vb(4) : error BC30529: All parameters must be explicitly typed if any are.

Since the option of 'strict' is switched off, we are not required to specify the data types of the
parameters. They default to type Object.

The erratic and fastidious behavior witnessed above, is the fallout of having specified the type
for only one parameter, and not for all of them. You should either specify the types for all the
parameters or for none of them. Any half-baked action results in an error being hurled at us.

a.vb
class zzz
shared Sub Main()
End sub
function abc( abc as integer)
end function
End class

Error
c:\il\a.vb(4) : error BC30530: Parameter cannot have the same name as its defining function.

There are subtle differences between functions and subs. In a sub, the parameters can have
any name, but this is not true of a function. You may recall that in a function, there are two
ways of returning values; The first is by using the return statement, and the second is by
simply initializing the name of the function.

If a parameter is assigned the same name as a function, the compiler is sure to feel baffled.
This is because, it is an extremely arduous task to ascertain whether the programmer is setting
the return value, or is changing the value of the parameter. This error applies to functions
only, since subs do not return values.

a.vb
class zzz
shared Sub Main()
dim j as integer = 100
dim a as new zzz
a.abc(j)
System.Console.WriteLine(j)
j = 100
a.abc((j))
System.Console.WriteLine(j)
End sub
sub abc( byref i as integer)
i = 10
end sub
End class

Output
10
100

When parameters are passed by reference, the changes made in the sub are also reflected in
the original variable. Therefore, the first call to sub abc changes the value of j from 100 to 10.
So far, so good!

After resetting the value of j back to 100, the sub abc is called again, but this time, j is
enclosed within a pair of round brackets. The brackets are a subtle way of informing the
compiler that the parameter is being passed by value and not by reference. Thus, the second
call, which is a call by value, does not lead to a change in the value of the variable j. Even if the
parameter i is set to byval, the use of round brackets shall not cause any error.

When it comes to passing parameters, there are four distinct possibilities to mull over. The
parameters can either be 'byval' or 'byref' types, or they may be 'value' or 'reference' types.

It is easiest to understand the concept of passing parameters by value. Here, any change in the
value of the variable in the procedure, is not reflected in the original variable.
Passing parameters by reference is very distinct from the above. The value contained in the
original variable, changes with any change introduced in the variable in the sub. Reference
type variables are actually pointers to the original data.

A 'pass by value' affects only the value of the variable or the member, without affecting the
original variable; whereas, a 'pass by ref' changes both, the object, i.e. the original variable it is
pointing to, as well as, its members.

The advantage of 'pass by ref' is that the calling sub can change the value of the parameters.
There is no significant overhead in passing a variable either by ref or by value. However, it is
advantageous to pass larger value types such as structures by ref, and not by value, or else, a
large number of variables will have to be copied on to the stack.

Optional Arguments

a.vb
class zzz
shared Sub Main()
dim a as new zzz
a.abc(100,200)
a.abc(100,10)
a.abc(50)
End sub
sub abc( i as integer , optional j as integer = 10)
System.Console.WriteLine("{0} {1}", i , j)
end sub
End class

Output
100 200
100 10
50 10

At times, there exist a large number of parameters to a procedure. This is where the concept of
optional parameters comes handy.

The sub abc has two parameters, i and j. The parameter j is assigned a value of 10. It is
optional, since it is tagged with the 'optional' keyword.

However, the sub abc can be called with two parameters, 100 and 200, as a result of which,
the 'optional' keyword is devoid of any effect at all. The default value is overwritten with the
new value of 100. Hence, the output shows the value of j as 200.

The second call to the sub abc results in assigning the default value of 10 to the second
parameter. However, there is no technique for notifying the sub abc about whether the call has
been made with two parameters or with a single one. So, it behaves like it did earlier, and
reassigns the value of 10 to the second parameter.

The last call to sub abc is made with one parameter only. Therefore, the first parameter i
assumes this new value of 50, thus, resulting in the second parameter j assuming its default
value of 10.

The 'optional' keyword proves to be useful, since the user is not required to call the procedure
with all its parameters. The parameters that have been overlooked are assigned their default
values. However, the sub has no way of identifying the parameters that it has been called with.
Now, replace the above sub statement with the following:

sub abc( i as integer , optional j as integer )

Error
c:\il\a.vb(8) : error BC30812: Optional parameters must specify a default value.

The optional parameter must have a default value, or else, the very essence of the optional
keyword is lost. This is the only way of assigning a value to the optional parameter.

a.vb
class zzz
shared Sub Main()
End sub
sub abc( i as integer = 10)
end sub
End class

Error
c:\il\a.vb(4) : error BC32024: Default values cannot be supplied for parameters that are not
declared 'Optional'.

The reverse is also true. A parameter cannot be assigned a default value, unless it is prefixed
with the 'optional' keyword.

a.vb
class zzz
dim k as integer = 10
shared Sub Main()
End sub
sub abc( i as integer , optional j as integer = k )
end sub
End class

Error
c:\il\a.vb(5) : error BC30059: Constant expression is required.

The second condition imposed on optional arguments is that, the value has to be a constant
expression, which can be determined at compile time. Thus, the instance variable k, which has
a value of 10, is not declared as a constant, since its value can be changed before invoking the
sub abc. Thus, an error is thrown.

a.vb
class zzz
dim k as integer = 10
shared Sub Main()
End sub
sub abc( i as integer , optional j as integer = i )
end sub
End class

Error
c:\il\a.vb(5) : error BC30451: Name 'i' is not declared.

Furthermore, other parameters cannot be used to supply a value to the default parameter. The
other parameters are not visible to each other, as has been pointed out by the error message.

a.vb
class zzz
shared Sub Main()
End sub
sub abc( optional i as integer = 10, j as integer )
end sub
End class

Error
c:\il\a.vb(4) : error BC30202: 'Optional' expected.

The last rule is that, if we make any one parameter optional, all the parameters following it
have to be made optional. Therefore, since the first parameter i has been made optional, the
parameter j, which follows i, must also be made optional.

a.vb
class zzz
shared Sub Main()
dim a as new zzz
a.abc(100,,1000)
End sub
sub abc( i as integer , optional j as integer = 10 , optional k as integer = 30)
System.Console.WriteLine("{0} {1} {2}",i,j,k)
end sub
End class

Output
100 10 1000

In the above example, the second and the third parameters are declared as optional. While
calling the abc sub, we intend to skip the second parameter, but we would like to specify the
third parameter. This is obtained by not specifying any value between the commas, as revealed
above. Use of optional parameters is another way of overloading variables, where the data type
is the same.

a.vb
class zzz
shared Sub Main()
dim a as new zzz
End sub
sub abc( i as integer , optional j as integer = 10 )
end sub
sub abc( i as integer )
end sub
End class

Error
c:\il\a.vb(5) : error BC30300: 'Public Sub abc(i As Integer, [j As Integer = 10])' and 'Public Sub
abc(i As Integer)' cannot overload each other because they differ only by optional parameters.

The above error emanates from the fact that the two abc subs that have been overloaded, differ
only in the second sub, which has an optional parameter. We had mentioned earlier that
procedures can share the same name, but must have different parameters. They would be
deemed to be distinct, since the method signatures would vary.

In the above case, we can have both the subs abc, with the same single integer as a parameter.
However, the compiler would be thoroughly bewildered as to which abc to call. Therefore, it
would report an error.

The first sub abc can be called with either one, or two parameters, while the second sub abc is
to be called only with one parameter. But, when the sub abc is called with one single
parameter, the compiler is awe-struck, as both the subs fit the bill. Observe that the default
values in the error message are placed within square brackets.

a.vb
class zzz
shared Sub Main()
dim a as new zzz
End sub
sub abc( i as integer , optional j as integer = 10 )
end sub
sub abc( i as integer , optional j as string = "hi")
end sub
End class

Error
c:\il\a.vb(5) : error BC30696: 'Public Sub abc(i As Integer, [j As Integer = 10])' and 'Public Sub
abc(i As Integer, [j As String = "hi"])' cannot overload each other because they differ only by the
types of optional parameters.

The error appears much the same as the one shown above. Both the abc subs have the first
parameter as an integer, but the second one is optional. The types of the second parameter are
different. The ambiguity arises when the sub abc is called with a single parameter, since the
VB compiler realizes that both the abc subs can be executed under such a situation.

a.vb
class zzz
shared Sub Main()
dim a as new zzz
End sub
End class
class yyy
overridable sub abc( i as integer , optional j as string = "hi")
end sub
end class
class xxx
inherits yyy
overrides sub abc( i as integer , optional j as string = "bye")
end sub
end class

Error
c:\il\a.vb(12) : error BC30307: 'Public Overrides Sub abc(i As Integer, [j As String = "bye"])'
cannot override 'Public Overridable Sub abc(i As Integer, [j As String = "hi"])' because they differ
by the default values of optional parameters.

The error is generated because the sub in the base class has an optional value of "hi", while the
sub in the derived class has an optional value of "bye".

Overriding a method in the base class calls for the derived class to be identical in all respects,
including the default values. The same concept applies to overriding and overloading also.
Hence, we will not reiterate them here.

a.vb
class zzz
shared Sub Main()
dim a as new zzz
End sub
End class
class yyy
overridable sub abc( byval i as integer)
end sub
end class
class xxx
inherits yyy
overrides sub abc( byref i as integer)
end sub
end class

Error
c:\il\a.vb(12) : error BC30398: 'Public Overrides Sub abc(ByRef i As Integer)' cannot override
'Public Overridable Sub abc(i As Integer)' because they differ by a parameter that is marked as
'ByRef' versus 'ByVal'.

While we are converging around the subject of overloading methods, it would be pertinent to
mention that the above error creeps in, since the two subs abc have the parameter i specified
as byval in the base class, and as byref in the derived class. They must be either byval or byref
in both, the base class, as well as the derived class.

a.vb
class zzz
shared Sub Main()
End sub
End class
class yyy
sub abc( i as integer , optional j as aaa = 12)
end sub
end class
structure aaa
dim i k as integer
end structure

Error
c:\il\a.vb(6) : error BC31405: Optional parameters cannot have structure types.

The optional parameters cannot have a type of a structure specified for them. Since the
optional parameter in the sub is specified to be of type structure aaa, the error is reported.

So far, we have been passing parameters by position. This is because, we have been passing
them in the same sequence as they had been defined in the sub or function. Now, we shall
pass them by name instead.

a.vb
class zzz
shared Sub Main()
dim a as new zzz
a.abc(1,"hi",k:=2)
a.abc(20,j:="no",k:=10)
a.abc(i:=200,j:="no",k:=20)
End sub
sub abc( i , j , k )
System.Console.WriteLine("{0} {1} {2}",i,j,k)
end sub
End class

Output
1 hi 2
20 no 10
200 no 20

The sub abc takes three parameters, i, j and k. We can pass them either by position, i.e. by
specifying the parameters as we have been doing so far, or by employing the 'pass by name'
method. In this method, we use the name of the parameter, followed by the := sign, and then,
followed by the actual value. The sub that is called has no means of identifying the method
that has been used.
a.abc(200 , k := 20 , "no")

c:\il\a.vb(6) : error BC30241: Named argument expected.

We do not have 'carte blanche', i.e. complete discretion, to act in any manner that we desire.
The basic rule in VB is that, once we initiate the process of passing parameters using the
concept of 'call by name', we cannot switch to 'call by position' midway. We have to stick to the
same method till the end of the program.

The single biggest benefit of 'call by name' comes to the fore, when a method has a vast
number of optional parameters. 'Call by name' is easier to implement, since there is no need to
place inessential commas between blank parameters. Whether we are passing parameters by
value or by name, the requisite parameters have to be specified.

a.vb
class zzz
shared Sub Main()
End sub
shared sub new( i as integer)
end sub
End class

Error
c:\il\a.vb(4) : error BC30479: Shared 'Sub New' cannot have any parameters.

While we are on the subject of parameters, we wish to enlighten you about the fact that the
static constructor cannot have any parameters. This is because, the static constructor is called
at the time of loading the object, and at this stage, no control can be exercised over it.

a.vb
class zzz
shared Sub Main()
End sub
Public Property aa() As Integer
Get
End Get
Set (byref i As Integer)
End Set
End Property
End class

Error
c:\il\a.vb(7) : error BC31065: 'Set' parameter cannot be declared 'ByRef'.

A property parameter in the set cannot have a byref, since it is not allowed to change the value
of the original variable. The Set accessor is called, whenever the value of the property has to be
changed. It is not used to change the value of the variable that sets the value of the property.

a.vb
class zzz
shared Sub Main()
End sub
Public default Property aa() As Integer
Get
End Get
Set (i as integer)
End Set
End Property
End class

Error
c:\il\a.vb(4) : error BC31048: Properties with no required parameters cannot be declared
'Default'.

A default property or an indexer requires a parameter, since that is the only available means of
using it as an index into an array of values.

a.vb
imports system
class zzz
shared Sub Main()
dim a as new zzz
a.abc("Hi",10,20,30)
a.abc("Bye")
Dim b() As integer = {100,200}
a.abc("No", b)
End sub
sub abc( k as string, paramarray s() as integer)
dim i,j as integer
j = s.GetUpperBound(0)
System.Console.WriteLine("{0} {1}" , k , j)
For i = 0 To s.GetUpperBound(0)
System.Console.Write(s(i) & " ")
Next i
System.Console.WriteLine()
end sub
End class

Output
Hi 2
10 20 30
Bye -1
No 1
100 200

The program has a procedure that acts like a WriteLine function, where a multiple number of
parameters can be specified. Normally, the hawk-eyed VB compiler ensures that the number of
parameters allotted to the procedure, does not exceed its limit. The only way of ascertaining
this is by using a parameter array.

The sub abc takes two parameters, i.e. a string and a paramarray of type integer. The
paramarray keyword stands for an 'array of parameters', which can take a varying number of
arguments. The first call to the sub abc takes one string and three individual integers. The
string value is assigned to the string parameter, and the three integers are stored in 's', which
is a paramarray type. Thus, 's' now becomes an array of three integers.

The array type has a function called GetUpperBound, which returns the largest dimension or
upper bound of the size of the array. The parameter 0 is a dimension parameter. Since the
array comprises of 3 members, the function returns 2, which is 1 less than the size.
We display the return value of this function, and then, use the 'for next' loop to flaunt the
individual members. Had the GetUpperBound function returned the actual size of the array, we
would then have had to subtract 1 from the value contained in variable j. This loop prints out
the individual members of the array. The sub abc is then called with only one parameter. The
value of -1 returned by the GetUpperBound function signifies that the value is nothing or null.

In the third case, we create an array 'b' of two integers, and pass only this array as a
parameter. The sub abc is oblivious to whether we are passing an array or individual values.
However, the end result is that we obtain an array containing all the integers.

This is the manner in which we can handle a varying number of arguments. Let us consider
the restrictions that are imposed upon parameter arrays.

a.vb
class zzz
shared Sub Main()
End sub
sub abc( k as string, paramarray s() as integer , paramarray t() as string)
end sub
End class

Error
c:\il\a.vb(4) : error BC30192: ParamArray parameter must be last in parameter list.

We can have only one paramarray per method. The above error has come about, since there
exist two paramarrays of different types, which is not permissible.

The second condition is that the paramarray has to be the last parameter in the sequence.

a.vb
class zzz
shared Sub Main()
End sub
sub abc( k as string, byref paramarray s() as integer )
end sub
End class

Error
c:\il\a.vb(4) : error BC30667: ParamArray parameters must be declared 'ByVal'.

The third restriction is that the paramarray has to be passed as byval, and not as byref. This is
very obvious, since if both were allowed, the system would have had to figure out which of the
original variables can be modified. The documentation recommends the use of the byval
keyword, even though it is the default value. The sub with the paramarray parameter receives a
one-dimensional array. The default is an empty array of the paramarray data type.

a.vb
class zzz
shared Sub Main()
End sub
sub abc( optional k as string = "hi", paramarray s() as integer )
end sub
End class
Error
c:\il\a.vb(4) : error BC30046: Method cannot have both a ParamArray and Optional parameters.

Life is full of 'either-or' choices. If we use the 'optional' keyword, the 'paramarray' keyword
cannot be specified. Thus, we can have either 'optional' or 'paramarray' parameters.

a.vb
class zzz
shared Sub Main()
dim a as new zzz
a.abc(s:=10)
End sub
sub abc(paramarray s() as integer )
end sub
End class

Error
c:\il\a.vb(4) : error BC30587: Named argument cannot match a ParamArray parameter.

There is no way by which a named argument can be used to specify a paramarray parameter.
This is because, the paramarray parameters are varying in number, and they have to be
separated by spaces. This surely would confound the compiler. Hence, only parameters by
position can be used.

a.vb
class zzz
shared Sub Main()
End sub
sub abc(paramarray s as integer )
end sub
End class

Output
c:\il\a.vb(4) : error BC30050: ParamArray parameter must be an array.

The paramarray parameter must be declared as an array, since its job is to store the multiple
values that can be theoretically passed.

a.vb
class zzz
shared Sub Main()
End sub
sub abc(paramarray s(,) as integer )
end sub
End class

Error
c:\il\a.vb(4) : error BC30051: ParamArray parameter must be a one-dimensional array.

The second condition is that the paramarray can only be a one-dimensional array.

An overloaded procedure must satisfy certain criteria, i.e. the overloaded procedures must have
the same name, or else, overloading makes no sense. Since the names are identical, the
overloaded procedures must differ from each other in three different ways:
• The first is that the procedures must have different number of arguments.
• The second is in respect of the order of the arguments.
• The third is relates to the data types of the arguments.

The three aforementioned aspects put together, are collectively called the 'signature' of the
procedure.

The modifiers of 'pubic', 'shared' and 'static' do not form a part of the procedure signature. The
other entities that do not belong to the method signature are argument names, keywords like
'byref' and 'optional', and the return data types of the functions.

a.vb
class zzz
shared Sub Main()
End sub
End class
class aaa
overloads function abc() as integer
end function
overloads function abc() as string
end function
end class

Error
c:\il\a.vb(6) : error BC30301: 'Public Overloads Function abc() As Integer' and 'Public Overloads
Function abc() As String' cannot overload each other because they differ only by return types.

The above error crops up, since the two abc functions encompass the same parameter lists, but
they return an integer in one case, while a string in the other. The return value of a function is
not mission-critical, since the value need not be stored.

Therefore, functions with different return values cannot be overloaded. However, functions that
overload each other with different argument lists may have different return values.

a.vb
class zzz
shared Sub Main()
End sub
End class
class aaa
overloads sub abc()
end sub
overloads function abc() as string
end function
end class

Error
c:\il\a.vb(6) : error BC31073: 'abc' conflicts with a function by the same name declared in 'aaa'.

The aforementioned error is tossed at us because the sub abc does not take any parameters,
and simultaneously, the function abc that ensues, is also devoid of any parameters. Thus, the
compiler is unable to discriminate between the sub and the function, since they both have the
same signature.
By assigning the function and the sub with different parameter lists, the error ebbs off.
Internally, VB does not distinguish between subs and functions. If a function and a property
both have the same name, the above error is reported.

For the sake of revision, we repeat that whenever a function like abc( i as integer, optional j as
string = "hi") is created, in effect, we are creating two functions: one with a single integer and
the other with an integer and a string. Therefore, another function with the above signature
cannot be created. These rules get more complicated when multiple argument lists are
involved.

a.vb
class zzz
shared Sub Main()
dim a as new zzz
a.abc(10)
a.abc(10,20)
a.abc(10,20,30)
End sub
overloads sub abc(paramarray i() as integer)
System.Console.WriteLine("paramarray")
end sub
overloads sub abc(i as integer )
System.Console.WriteLine("single")
end sub
End class

Output
single
paramarray
paramarray

The above example demonstrates the complexities associated with the paramarray during
'procedure overloading'. There are two subs called abc. However, since the signatures for all of
them are different, no compiler errors are generated.

The signatures are different because, the first sub takes a paramarray, while the second one
takes a single integer. When the sub abc is called with a single integer, there is bound to be a
dilemma as to which of the subs is to be called, since they both fit the bill.

In the first call, the system prefers the single sub with an integer as parameter. For the second
and the third calls of the sub abc, the system calls the sub with the single paramarray, since
the sub with the single parameter does not fit the bill.

We have tried to introduce one more definition in

overloads sub abc(i as integer , paramarray j() as integer)


System.Console.WriteLine("two")
end sub

We had hoped that it would correspond with all the three calls. However, we land up with
errors galore, as the compiler gets utterly baffled!

A sub with a paramarray parameter represents at least three subs. The first is a sub that has a
single dimensional array, the second is a sub with no parameter, and a third is a sub that has
a series of individual parameters, of the data type of paramarray.
Before concluding this treatise on procedures, let us examine a few error messages that we
have not touched upon so far.

a.vb
class zzz
shared Sub Main()
End sub
sub abc()
exit property
end sub
End class

Error
c:\il\a.vb(5) : error BC30066: 'Exit Property' is not valid in a Function or Sub.

Unlike a return statement, an Exit property statement cannot be used to quit out of a property.
In the same way, we cannot replace the word 'property' with 'function'. It will cast an error, as
depicted below:

C:\il\a.vb(5) : error BC30067: 'Exit Function' is not valid in a Sub or Property.

a.vb
class zzz
shared Sub Main()
End sub
function New
end function
End class

Error
c:\il\a.vb(4) : error BC30493: Constructor must be declared as a Sub, not as a Function.

The constructor must be declared as a sub and not as a function, since constructors do not
return values. By defining them as functions, we are explicitly stating that they do return
values.

a.vb
class zzz
shared Sub Main()
End sub
sub New handles e.abc
end sub
End class
Error
c:\il\a.vb(4) : error BC30497: 'Sub New' cannot handle events.

The constructor cannot handle any events, since it can never be called explicitly. Thus, using
the 'handles' keyword with it generates a compiler error. A constructor's singular task is to get
called only at the time of creation of an object. Thus, it cannot handle events.

a.vb
class zzz
shared Sub Main()
End sub
sub new implements yyy.abc
end sub
End class

Error
c:\il\a.vb(4) : error BC31042: 'Sub New' cannot implement interface members.

The constructor is also unable to implement any interface functions.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
sub new
me.new
end sub
End Class

Error
c:\il\a.vb(4) : error BC30297: Constructor 'Public Sub New()' cannot call itself:

A method is capable of calling itself. This is a well-known concept called 'recursion'. A


considerable number of computer problems are resolved by using recursion. However, a
constructor cannot call itself. If it does so, the above error is hurled at us.

a.vb
class zzz
shared Sub Main()
End sub
End class
class aaa
sub new
end sub
sub new (paramarray a() as integer)
end sub
end class
class bbb
inherits aaa
end class

Error
c:\il\a.vb(11) : error BC32036: Class 'bbb' must declare a 'Sub New' because its base class 'aaa'
has more than one accessible 'Sub New' that can be called with no arguments.

The class aaa has two constructors, one without any parameters, and the other with a
paramarray, which also expects no parameters. The class bbb is derived from the class aaa.
This gives out the above error, as there are two 'new' subs in the base class, and they both
cannot be called with parameters. To dodge this error, simply add a sub new to the class bbb,
as follows:

class bbb
inherits aaa
sub new
end sub
end class
Error
c:\il\a.vb(13) : error BC32038: First statement of this 'Sub New' must be a call to 'MyBase.New'
or 'MyClass.New' because base class 'aaa' of 'bbb' has more than one accessible 'Sub New' that
can be called with no arguments.

This gives rise to a different error, since the addition of a sub in class bbb does not resolve the
earlier error of identifying the sub 'new' as being called from the base class.

sub new
mybase.new
end sub

However, by adding a call to the base class constructor, the error retires from sight.

a.vb
class zzz
shared Sub Main()
End sub
End class
class yyy
shared Sub Main()
End sub
End class

Error
vbc : error BC30738: 'Sub Main' is declared more than once in 'a': zzz.Main(), yyy.Main()

There cannot be more than one occurrence of the main sub in the program. The above error
substantiates this fact. Neither of the two classes zzz or yyy can contain a main sub in them. It
is because, the system would not know as to which sub main to call, while executing the
program.

a.vb
Public Class zzz
Shared Sub Main()
End Sub
function vijay()
dim vijay as integer
End function
End Class

Error
c:\il\a.vb(5) : error BC30290: Local variable cannot have the same name as the function
containing it.

A variable and a function cannot have the same name, since it would create an imbroglio when
the variable has to be initialized. A similar error message occurs when a function and a
parameter share the same name. This has already been explained earlier in the chapter.

There are certain error messages that never get generated. One of them is 'Identifier is too long,
since the largest size of an identifier is 16384 characters'. We obviously do not have the time to
create a variable as sizeable as this.
Another error that cannot be displayed is, "Maximum number of errors has been exceeded".
This is because, the compiler will stop only when it encounters a total of 100 errors!

a.vb
class zzz
shared Sub Main()
+ 45
End sub
End class

Error
c:\il\a.vb(3) : error BC30035: Syntax error.

It is but natural to feel peeved at the petulant conduct of some people, and hence, there is a
desperate urge to let the steam off. The same is true of the compiler. When it bumps into an
error that does not fall into a predefined category, it reports it as a Syntax error.

a.vb
Public Class zzz
dim a as private integer
Shared Sub Main()
End Sub
End Class

Error
c:\il\a.vb(2) : error BC30180: Keyword does not name a type.

Every statement has its own set of rules. For instance, the Dim statement expects a type
following the 'as' keyword. Since 'private' is not a type but a keyword, the above error message
is generated.

a.vb
Public Class zzz
Shared Sub private Main()
End Sub
End Class

Error
c:\il\a.vb(2) : error BC30183: Keyword is not valid as an identifier.

In the same vein, the sub requires the name of the sub or an identifier, whereas, it has actually
been furnished with the 'private' keyword. An identifier cannot be a keyword.

a.vb
Public Class zzz
a as integer
Shared Sub Main()
End Sub
End Class

Error
c:\il\a.vb(3) : error BC30188: Declaration expected.

The VB program comprises of both, declarative and the nondeclarative statements. A


nondeclarative statement, such as an assignment or a loop, cannot occur outside a procedure.
Thus, whenever there is some gibberish present outside a procedure, the above error is thrown.
A similar error message is received when a variable is created without using the Dim
statement.

a.vb
for i = 1 to 10
Public Class zzz
Shared Sub Main()
End Sub
End Class

Error
c:\il\a.vb(1) : error BC30689: Statement cannot appear outside of a method body.

Similarly, the 'for' statement cannot be used outside a method. The error, even with a slight
difference, applies to all executable statements, and are permissible only within methods.

a.vb
Public Class zzz
Shared Sub Main()
dim a as new zzz
a.abc(10 : 20)
End Sub
sub abc ( i as integer)
end sub
End Class

Error
c:\il\a.vb(4) : error BC30198: ')' expected.

The parameters to a sub or a function, must be separated by commas. If we use colons instead,
the above error is generated. Despite removing the colon, another error gets generated, which is
as follows:

Error
c:\il\a.vb(4) : error BC32017: Comma, ')', or a valid expression continuation expected.

a.vb
Public Class zzz
Shared Sub Main()
dim b(2) as integer = {1 2}
End Sub
End Class

Error
c:\il\a.vb(3) : error BC30370: '}' expected.

Bypassing the comma for the array would lead to yet another error, which states that the } sign
is expected. Also, the compiler casts another error, which states that an array cannot be
created and initialized at the same time, with its dimension specified.

a.vb
class zzz
shared Sub Main()
End sub
sub abc( s as integer, s as integer)
end sub
End class

Error
c:\il\a.vb(4) : error BC30237: Parameter already declared with name 's'.

There are some errors that are present for the sake of completeness, like the one just
discussed, where the two parameters have been assigned the same name.
 

S-ar putea să vă placă și