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.