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