
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
S