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

1 1 1

 

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.