Analytics


Google

Tuesday, May 26, 2009

Programmer's Journal - Chapter 3

Chapter 3 – Anatomy of a program

This chapter will introduce some of the basic anatomy of the program.

Namespace
Every DotNet program is called an assembly (irrespective if it is a WinForm application, Console Application or even a .DLL library). All DotNet codes are arranged in Namespaces. Even if your code does not declare a namespace, it is automatically placed into a default namespace. Namespace provides a way for you to declare classes and modules and without needing to make sure that the name has not been previously used (unless it will be in the same namespace). For example, you have buttons in Web Forms (System.Web.UI.WebControls.Button) ; you have also buttons in Windows Forms (System.Windows.Forms.Button). Without namespaces, this would not be possible.

In our examples of “Hello World”, we use System.Console.WriteLine to write the “Hello World” at the console.

Module Hello
Sub Main
System.Console.writeline("Hello World")
End Sub
End Module


It would be tedious to do that for every single code. Alternatively, the namespace can be imported, as shown below. However, if two classes (from different namespace) with the same name are used and both namespaces were imported, the fully qualified name must be used to avoid confusion. (Just as you would if you have two John in the room, you need a way to tell which John you are referring to).

Imports System

Module Hello
Sub Main
Console.writeline("Hello World")
End Sub
End Module


Modules and Classes
Module is a special type of class that cannot be instantiated – meaning all references to the members in the module refers to the same instance. All members (subroutine, variables, functions etc) of a module are implicitly shared. Also, you cannot inherit a module nor can you derive a class from a module. Therefore, if you have a change the value of a variable in the module, all codes that refer to the variable will see the same value. This is very important to remember especially if you are going to write in ASP.NET or any multiuser application. When more than one person access it, you will encounter strange results and error.

One way to overcome this is to pass the values as parameters to your functions and subroutines (as you would in shared subroutines and functions).

For classes, you can have multiple instances of a class. Variables within each instance are independent of other instances. As shown in the example below:

'
' Module and Class Sample
' User: Strovek
' Date: 8/28/2005
'
' ========================================

Imports System

Namespace PJ

Module Mod1
Dim vNumVar1 As Integer = 10

Public Property pIntVal As Integer
Get
Return vNumVar1
End Get
Set (Value As Integer)
vNumVar1 = Value
End Set
End Property
End Module

Module Mod2
Sub Main
Console.WriteLine("test program")
pIntVal = 5
Console.WriteLine("Mod1 integer value is {0}.", pIntVal)

Dim ObjC1 As New cls1
Dim ObjC2 As New cls1

Console.WriteLine("Obj 1 integer value is {0}.", ObjC1.pIntVal2)
Console.WriteLine("Obj 2 integer value is {0}.", ObjC2.pIntVal2)
ObjC1.pIntVal2 = 9
ObjC2.pIntVal2 = 20
Console.WriteLine("Obj 1 integer2 value is {0}.", ObjC1.pIntVal2)
Console.WriteLine("Obj 2 integer2 value is {0}.", ObjC2.pIntVal2)

Console.WriteLine("Obj 1 integer3 value is {0}.", ObjC1.pIntVal3)
Console.WriteLine("Obj 2 integer3 value is {0}.", ObjC2.pIntVal3)


End Sub
End Module

Public Class cls1
Dim vNumVar1 As Integer = 10

Public Sub New()
End Sub

Public Property pIntVal2 As Integer
Get
Return vNumVar1
End Get
Set (Value As Integer)
vNumVar1 = Value
End Set
End Property

Public Function pIntVal3 As Integer
return pIntVal
End Function

End Class
End Namespace


The output is as follows:



The following lines of code will produce the same output:

Console.WriteLine("Obj 1 integer value is {0}.", ObjC1.pIntVal2)
Console.WriteLine("Obj 1 integer value is " & ObjC1.pIntVal2 & ".")
exit



Setting and getting values of Variables within Classes

If you have some variables in a class, which you want to be able to obtain values as well as change the values. There are a few ways of doing it:

1. Declare the variable as public.
2. Have a Subroutine to set the value and have a function to return the value.
3. Create a property for setting and getting the value.

Option 1 is the easiest method but there are several problems with it. You have no control on how the values are set and neither do any classes that inherit from this class.

Option 2 and 3 provides much better flexibility. You can:

1. Add additional logic to control values you can set for the variable.
2. Allow the derived class to change the logic.
3. Make the variable read only from the outside by not declaring the subroutine for setting the value or the property for setting the value (while still allow the value to be changed during initializing, or by other subroutines or functions within the class).

The following is a sample of the above:

'
' Module and Classes Sample
' User: Strovek
' Date: 8/28/2005
'
' ========================================

Imports System

Namespace PJ

Module Mod1
Dim vNumVar1 As Integer = 10

Public Property pIntVal As Integer
Get
Return vNumVar1
End Get
Set (Value As Integer)
vNumVar1 = Value
End Set
End Property
End Module

Module Mod2
Sub Main
Console.WriteLine("test program")
pIntVal = 5
Console.WriteLine("Mod1 integer value is {0}.", pIntVal)

Dim ObjC1 As New cls1
Dim ObjC2 As New cls1

Console.WriteLine("Obj 1 integer value is {0}.", ObjC1.pIntVal2)
Console.WriteLine("Obj 2 integer value is {0}.", ObjC2.pIntVal2)
ObjC1.pIntVal2 = 9
ObjC2.pIntVal2 = 20
Console.WriteLine("Obj 1 integer2 value is {0}.", ObjC1.pIntVal2)
Console.WriteLine("Obj 2 integer2 value is {0}.", ObjC2.pIntVal2)

Console.WriteLine("Obj 1 integer3 value is {0}.", ObjC1.pIntVal3)
Console.WriteLine("Obj 2 integer3 value is {0}.", ObjC2.pIntVal3)


End Sub
End Module

Public Class cls1
Dim vNumVar1 As Integer = 10

Public Sub New()
End Sub

Public Property pIntVal2 As Integer
Get
Return vNumVar1
End Get
Set (Value As Integer)
vNumVar1 = Value
End Set
End Property

Public Function pIntVal3 As Integer
return pIntVal
End Function

End Class
End Namespace



The output is as follows:



In Sub Var3 and Property Var4, if the value passed is more than 50, set the value to –1.

Constants

Constants can (declared at the top of the program and) be used to provide a fixed value that can be used in various parts of the program. This will ease maintenance by allowing single point of modification when the value changes. It can also be used to make the code more readable by replacing numeric code with a readable name for example example vbNewLine to represent a CrLf, ConnectionState.Open is easier to understand than the numeric code it represents.

Classes Inheritance and Overriding

VB.Net is an Object Oriented Programming Language. As such, it supports inheritance.

A subroutine or function can be overwritten only if it has the keyword Overridable – the new class can then declare the new subroutine and function (with the same name) using the keyword Overrides.

Constants must be declared as Friends before it can be visible to the derived class. If the derived class wishes to overrides the value of the constant, then the new constant needs to be declared as Shadows.

The following is an example:

' Inheritance Sample
' User: Strovek
' Date: 8/28/2005
'
' ========================================

Imports System

Namespace PJ

Public Class clsOr
Friend Const preFixStr As String = "Sub Routine"

Public Overridable Function dispSub1 As String
return preFixStr & " 1"
End function

Public Overridable Function constVal As String
Return preFixStr
End Function

Public Sub New()
End Sub
End Class

Public Class clsNew
Inherits PJ.clsOr

Public Overrides Function dispSub1 As String
Return preFixStr & " 1 - Changed"
End Function

End Class

Public Class cls2ndGen
Inherits clsNew

Shadows Const preFixStr As String = "Another Value"

Public Overrides Function dispSub1 As String
Return "Unrelated value"
End Function

End Class

Public Class cls3rdGen
Inherits cls2ndGen

Shadows Const preFixStr As String = "New Value"

Public Overrides Function dispSub1 As String
Return "Unrelated value 2"
End Function

Public Overrides Function constVal As String
Return preFixStr
End Function

End Class

Module Mod1
Public Sub Main
Console.WriteLine("Program Start")
Dim Obj1 As New clsOr
Dim Obj2 As New clsNew
Dim Obj3 As New cls2ndGen
Dim Obj4 As New cls3rdGen

Console.WriteLine("Obj 1 constVal is {0}", obj1.constVal)
Console.WriteLine("Obj 2 constVal is {0}", obj2.constVal)
Console.WriteLine("Obj 3 constVal is {0}", obj3.constVal)
Console.WriteLine("Obj 4 constVal is {0}", obj4.constVal)


Console.WriteLine("Obj 1 sub 1 is {0}", obj1.dispSub1)
Console.WriteLine("Obj 2 sub 1 is {0}", obj2.dispSub1)
Console.WriteLine("Obj 3 sub 1 is {0}", obj3.dispSub1)
Console.WriteLine("Obj 4 sub 1 is {0}", obj4.dispSub1)

End Sub

End Module

End Namespace


In cls2ndGen class, even though the value of constVal was changed to “preFixStr”, the constVal still displays the original value. This is because we are using the constVal inherited from clsOr (the original class). When writing Windows Form, Web Services and ASP.Net applications; inheriting the appropriate classes will make programming much easier.

Inheriting a class provides the option to override existing methods (subroutine or functions) or using the original implementation. That is the true power of inheritance – allowing building on the existing code (instead of starting from scratch).
Main Subroutine

When writing a console program, one shared subroutine called main is necessary. Adding the (args() as String) parameter will allow the program to accept runtime parameters.

'
' Program with Parameters
' User: Strovek
' Date: 8/30/2005
'
' ========================================
Imports System

Module PrgWithParam

Sub Main(args() As String)
Console.Writeline("Start Program")
Console.WriteLine("Total Paramater is {0}.", args.Length)

Dim loopCnt As Integer

For loopCnt = 0 To args.Length-1
Console.WriteLine("Arg {0} is {1}.", loopCnt, args(loopCnt))
Next
End Sub
End Module


The output is as follows:


When writing a Windows Form, a main subroutine is optional. However, a Windows Form application must:

1. Inherit from a System.Windows.Forms (or another derived class). So you cannot use a module to create program.
2. Import System.Drawing (since we will be using a lot of graphics – which we did not need when we wrote a text based (console) application.
3. Perform a System.Windows.Forms.Application.Run the form object.

The following sample will create a blank window:

'
' Simple Windows Form
' User: Strovek
' Date: 8/30/2005
'
' ========================================

Imports System
Imports System.Drawing
Imports System.Windows.Forms

Namespace PJ

Public Class wForm
Inherits Form

Public Shared Sub Main()
Dim winObj As New wForm
application.Run(winObj)
End Sub
End Class
End Namespace


The output is as follows:



For the windows form, the shared main subroutine is optional. If you leave out the shared main subroutine is not available, the main class must be specified using /main: when compiling the exe.

The modified code is as follows (the output is identical with the code above) – I added the namespace to illustrate that the class needs to be a fully qualified class, if no namespace is there then you specify /main:wForm.

'
' Simple Windows Form
' User: Strovek
' Date: 8/30/2005
'
' ========================================

Imports System
Imports System.Drawing
Imports System.Windows.Forms

Namespace PJ

Public Class wForm
Inherits Form

End Class
End Namespace

The compile the above program, we need to type the following (all in one line):

\WINDOWS\Microsoft.NET\Framework\v1.1.4322\vbc /t:winexe SampleWindowForm2.vb /main:PJ.wForm /r:System.Windows.Forms.dll,System.dll,System.Drawing.dll

The following is how the process looks like:





Events

In Window Form and ASP.Net application, majority of the work is done through event handling. Most of the controls have built in events (e.g. Buttons have Click, Form contains load etc).

There are two ways of handling events:

1. Create a subroutine and specify that it “handles the event”.
2. Adding the handler to the event

In the samples below, you will notice that all subroutines handling the events have to be declared with an object and also an eventargs as parameters. This is because the event will pass both these parameters to the event handler subroutine.
Note that you can have more than one subroutine handle a specific event and a subroutine handle multiple events.

Direct event handling
In this approach, just add the keyword “WithEvents” to the object declaration as in:

WithEvents Private btnSubmit2 As System.Windows.Forms.Button

Then declare the subroutine as handling the event as follows:

Private Sub BtnSubmitClick(sender As System.Object, _
e As System.EventArgs) handles btnSubmit.Click
MsgBox("Button Clicked")
End Sub


Adding the handler to the event

In this approach, the “WithEvents” keyword is optional in the object declaration:

Private btnSubmit2 As System.Windows.Forms.Button

Add the handler to the event using AddressOf creates a procedure delegate instance that references the specific procedure. It is a pointer associated to the procedure:

AddHandler Me.btnSubmit2.Click, AddressOf Me.BtnSubmit2Click

Once done, it is not necessary to specify the event in the subroutine declaration, as shown below:

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

The following code illustrates the above concept. There are three buttons – two buttons are declared with “WithEvents” keyword. btnSubmit and btnSubmit3 have two handlers. Subroutine MultiHandler handles two events. The sender can be converted to the original control using CType Function, (otherwise gettype will return the type). In this case, we know that the sender is a button control, so it can be converted back to its original control in order to obtain the name of the sender.

'
' Sample Windows Form Events
' User: Strovek
' Date: 8/26/2005
'
' ========================================
Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports Microsoft.VisualBasic

Namespace TestWinform

Public Class MainForm
Inherits System.Windows.Forms.Form
WithEvents Private btnSubmit As System.Windows.Forms.Button
WithEvents Private btnSubmit3 As System.Windows.Forms.Button
Private btnSubmit2 As System.Windows.Forms.Button

Public Shared Sub Main
Dim fMainForm As New MainForm
fMainForm.ShowDialog()
End Sub

Public Sub New()
MyBase.New
'
' The Me.InitializeComponent call is required for
' Windows Forms designer support.
'
Me.InitializeComponent
'
' TODO : Add constructor code after InitializeComponents
'
End Sub

#Region " Windows Forms Designer generated code "
' This method is required for Windows Forms designer support.
' Do not change the method contents inside the source code
' editor. The Forms designer might
' not be able to load this method if it was changed manually.
Private Sub InitializeComponent()
Me.btnSubmit2 = New System.Windows.Forms.Button
Me.btnSubmit3 = New System.Windows.Forms.Button
Me.btnSubmit = New System.Windows.Forms.Button
Me.SuspendLayout
'
'btnSubmit2
'
Me.btnSubmit2.Location = New System.Drawing.Point(104, 88)
Me.btnSubmit2.Name = "btnSubmit2"
Me.btnSubmit2.TabIndex = 1
Me.btnSubmit2.Text = "Submit2"
AddHandler Me.btnSubmit2.Click, _
AddressOf Me.BtnSubmit2Click
'
'btnSubmit3
'
Me.btnSubmit3.Location = New _
System.Drawing.Point(104, 200)
Me.btnSubmit3.Name = "btnSubmit3"
Me.btnSubmit3.TabIndex = 2
Me.btnSubmit3.Text = "Submit3"
AddHandler Me.btnSubmit3.Click, _
AddressOf Me.BtnSubmit3Click
'
'btnSubmit
'
Me.btnSubmit.Location = New System.Drawing.Point(104, 144)
Me.btnSubmit.Name = "btnSubmit"
Me.btnSubmit.TabIndex = 0
Me.btnSubmit.Text = "Submit"
'
'MainForm
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(292, 266)
Me.Controls.Add(Me.btnSubmit3)
Me.Controls.Add(Me.btnSubmit2)
Me.Controls.Add(Me.btnSubmit)
Me.Name = "MainForm"
Me.Text = "MainForm"
Me.ResumeLayout(false)
End Sub
#End Region

Private Sub BtnSubmitClick(sender As System.Object, _
e As System.EventArgs) handles btnSubmit.Click
MsgBox("Button Clicked")
End Sub

Private Sub BtnSubmit2Click(sender As System.Object, _
e As System.EventArgs)
msgbox("Button 2 Clicked")

End Sub

Private Sub BtnSubmit3Click(sender As System.Object, _
e As System.EventArgs)
MsgBox("Button 3 Clicked")
End Sub

Private Sub MultiHandler(sender As System.Object, _
e As System.EventArgs) _
Handles btnSubmit.Click, btnSubmit3.Click
MsgBox("Button 1 or 3 has been clicked")
msgbox(sender.ToString & " sent this event:" & e.ToString)

Dim tmpBtn as System.Windows.Forms.Button = _
Ctype(sender, System.Windows.Forms.Button)
msgbox("the name is " & tmpbtn.Text)
End Sub

End Class
End Namespace

No comments: