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.