It used to be the case that Visual Basic was regarded as a "toy" language. VB was never treated seriously. This is partly due to the fact that VB does not contain certain advanced features, like pointers access and object-oriented programming. Not anymore! With the launch of the Microsoft .NET framework, Microsoft has catapulted the image of VB programmers to the rank of C and C++ programmers. In this article, I will show you the object-oriented (OO) features of VB.NET and how you can now be on par with your fellow C++ and C# programmers.
The first two terms that you must know are class and object. A class is like a template; it defines the innards of an item. A good analogy is "car." When the word "car" is mentioned, you have the impression of a vehicle with wheels, etc., but no specific make of car is mentioned. An object, in turn, is an instance of a class. A "BMW" is a specific make of a car and hence, it can be likened to an object.
In VB.NET, a class in defined by the following syntax:
Public Class Point End Class
To create an object from a class, you can use the
Dim pt As New Point()
In Listing 1, I have defined a
Public Class Point Private ptX, ptY As Integer Private Shared count As Integer Public Sub New() ' empty constructor count += 1 End Sub ' constructor with two parameters Public Sub New(ByVal x As Integer, ByVal y As Integer) ptX = x ptY = y count += 1 End Sub Property x() As Integer ' sets the X coordinate Get Return ptX End Get Set(ByVal Value As Integer) If Value < 500 And Value > -500 Then ptX = Value End If End Set End Property Property y() As Integer ' sets the Y coordinate Get Return ptY End Get Set(ByVal Value As Integer) ptY = Value End Set End Property ' calculates the length between 2 points Public Function length(ByVal pointOne As Point) As Single Return Math.Sqrt(Math.Pow(ptX - pointOne.x, 2) + _ Math.Pow(ptY - pointOne.y, 2)) End Function ReadOnly Property counter() As Integer ' returns the counter Get Return count End Get End Property End Class
Within the class, I have two private variables:
ptY. These two variables are only visible within the class.
Private ptX, ptY As Integer
I also have a shared variable:
count. This variable would be shared by all instances of the class. That is, all objects of this class would access the same variable.
Private Shared count As Integer
In my class, I have two constructors:
Public Sub New() ' empty constructor count += 1 End Sub ' constructor with two parameters Public Sub New(ByVal x As Integer, ByVal y As Integer) ptX = x ptY = y count += 1 End Sub
The use of a constructor is to initialize the values in an object when it is instantiated (created). A constructor is a subroutine prefixed with the keyword
New. The first constructor simply increments the count variable. The second constructor sets the value of the two private variables with the value of the input parameter.
There are a couple of ways to instantiate an object:
Dim pt1 As New Point(3, 4) Dim pt2 As Point = New Point(5, 6) Dim pt3 As New Point() Dim pt4 As Point
The first creates a
Point object and calls the second constructor shown earlier. It does so by matching the parameter list. The second instantiation statement is similar to the first. The third instantiation statement calls the first constructor shown above, but does not set any values in the object. The fourth simply creates an object of type
Point, but does not actually point to an object. To point to an actual object, it needs to do this:
pt4 = New Point()
Continuing with the class definition, I have also defined two properties,
y, using the
Property x() As Integer ' sets the X coordinate Get Return ptX End Get Set(ByVal Value As Integer) If Value < 500 And Value > -500 Then ptX = Value End If End Set End Property
The private variables are used to store the values set by the properties, as shown below:
'---setting the properties pt3.x = 7 ' stores the value 7 into private variable ptX pt3.y = 9 ' stores the value 9 into private variable ptY Dim x as Integer = pt3.x ' retrieving the value of ptX
Properties also provide a good opportunity to perform checking to ensure that the user does not assign an invalid value to a property.
I also have a method named
length, which returns the length between two points.
Public Function length(ByVal pointOne As Point) As Single Return Math.Sqrt(Math.Pow(ptX - pointOne.x, 2) + _ Math.Pow(ptY - pointOne.y, 2)) End Function
You can use the method through:
Note that this method is public; if it is defined to be a private method like the following, then it cannot be called as above.
Private Function length(ByVal pointOne As Point) As Single
Lastly, I have an additional property called
counter. Notice that this property has the keyword
ReadOnly. This keyword indicates that this property can only be read and cannot be assigned a value such as in the following:
Pt1.counter = 5 ' not allowed! Msgbox (Pt1.counter) ' allowed
In the first example, we have seen how a class can be defined and instantiated. In the next example, we will take a look at how classes can be inherited. Listing 2 shows the class definition of a class called
Public MustInherit Class Shape Dim l, b As Single Public MustOverride Function Area() As Single Public Overridable Function Perimeter() As Single Return 2 * (l + b) End Function Property length() Get Return l End Get Set(ByVal Value) l = Value End Set End Property Property breadth() Get Return b End Get Set(ByVal Value) b = Value End Set End Property End Class
Shape class contains two properties --
breadth. It also contains two methods --
Perimeter(). Let's take a more detailed look at the
Shape class now.
Shape class is defined with the
MustInherit keyword. The
MustInherit keyword indicates that some other classes must inherit the
Shape class; directly instantiating a
Shape class is not allowed. For example, the following is not allowed:
Dim someShapes as New Shape
Area() method definition contains the keyword
MustOverride keyword is used to define an abstract class. That is, the method itself does not contain any implementation; the classes that inherit from it must implement the actual codes.
Perimeter() method definition contains the keyword
Overridable. Although this method contains the actual implementation, any classes inheriting from it have the option to override its implementation.
Listing 3 shows the class
Rectangle inheriting from the
Shape class (using the
Inherits keyword). It implements the
Area() method using the
Overrides keyword. The keyword
MyBase refers to the base class (i.e.,
Public Class Rectangle Inherits Shape Public Overrides Function Area() As Single Return MyBase.length * MyBase.breadth End Function End Class
You can use the
Dim r As New Rectangle() r.length = 4 r.breadth = 5 MsgBox(r.Area) MsgBox(r.Perimeter)
Listing 4 shows the
Square class inheriting from the
Public NotInheritable Class Square Inherits Rectangle End Class
Square class contains the
NotInheritable keyword, which indicates that this class cannot be inherited. For example, the following is not allowed:
Public Class smallSquare Inherits Square End Class
You can use the
Dim s As New Square() s.length = 4 s.breadth = 4 MsgBox(s.Area) MsgBox(s.Perimeter)
Listing 5 shows the
Circle class. It overrides both the
Public Class Circle Inherits Shape Public Overrides Function Area() As Single Return Math.PI * Math.Pow(MyBase.length / 2, 2) End Function Public Overrides Function Perimeter() As Single Return 2 * Math.PI * MyBase.length / 2 End Function End Class
You can access the
Dim c As New Circle() c.length = 6 MsgBox(c.Area) MsgBox(c.Perimeter)
Another important concept in object-oriented programming is method overloading. Consider the example in Listing 6.
Public Class BaseClass Public Sub Init(ByVal num As Integer) MsgBox("Number in BaseClass is " & num) End Sub Public Sub Init(ByVal st As String) MsgBox("String in BaseClass is " & st) End Sub End Class
BaseClass contains two methods of the same name,
Init. However, these two methods accept different input parameters. One takes an integer, while the other takes a string.
In Listing 7, the class
DerivedClass inherits from the
BaseClass, and it too contains two methods called
Init, both prefixed by the
Public Class DerivedClass Inherits BaseClass Overloads Sub Init(ByVal num As Integer) ' overloads the method with the same parameter list MsgBox("Number in DerivedClass is " & num) End Sub Overloads Sub Init(ByVal ch As Char) ' overloads the method MsgBox("Character in DerivedClass is " & ch) End Sub End Class
The following examples should make it clear which methods are active:
'---Shadowing and overloading Dim d As New DerivedClass() ' prints out "String in BaseClass is Hello VB.NET" d.Init("Hello VB.NET") ' prints out "Number in DerivedClass is 5" d.Init(5) ' prints out "Character in DerivedClass is A" d.Init(Chr(65))
DerivedClass now exposes three methods with the following input parameters:
Public Sub Init(ByVal num As Integer) ' from DerivedClass Public Sub Init(ByVal ch As Char) ' from DerivedClass Public Sub Init(ByVal st As String) ' from BaseClass
In Listing 8, I have another class,
DerivedClass2, inheriting from
BaseClass. This time, I have the method
Init() prefixed with the
Public Class DerivedClass2 Inherits BaseClass Shadows Sub Init(ByVal num As Integer) ' hides all the different argument list MsgBox("Number in DerivedClass2 is " & num) End Sub End Class
Shadows keyword will effectively hide all of the other methods in the class, so now there is only one method exposed in the
DerivedClass2 class. The following illustrates this:
Dim d2 As New DerivedClass2() d2.Init(5) ' only one method is exposed
Multiple inheritances of classes are not allowed in VB.NET. That is, you cannot have something like this:
Public Class anotherClass Inherits BaseClass Inherits DerivedClass ' Not allowed! End Class
There are times when you do not want to code the implementation within a class. Interfaces provide a powerful mechanism to separate the definitions of objects from actual implementation. Listing 9 shows three interfaces. Note that only the definitions of methods and properties are present, not the implementation codes.
Interface Vehicle Sub Start() Sub Brake() Sub TurnLeft() Sub TurnRight() Sub Reverse() Sub Accelerate() Function GetSpeed() Property wheels() As Integer End Interface Interface Car Inherits Vehicle End Interface Interface Dimension Property length() As Single Property breadth() As Single End Interface
Listing 10 shows the
SportsCar class implementing (using the
Implements keyword) the
Dimension interfaces. A class that implements an interface must provide implementations for that interface.
Public Class SportsCar Implements Car Implements Dimension Public Sub start() Implements Car.Start ' implementation codes here End Sub Public Sub Brake() Implements Car.Brake ' implementation codes here End Sub Public Sub turnleft() Implements Car.TurnLeft ' implementation codes here End Sub Public Sub turnright() Implements Car.TurnRight ' implementation codes here End Sub Public Sub reverse() Implements Car.Reverse ' implementation codes here End Sub Public Sub accelerate() Implements Car.Accelerate ' implementation codes here End Sub Public Function getspeed() Implements Car.GetSpeed ' implementation codes here End Function Property wheels() As Integer Implements Car.wheels Get ' implementation codes here End Get Set(ByVal Value As Integer) ' implementation codes here End Set End Property Property breadth() As Single Implements Dimension.breadth Get ' implementation codes here End Get Set(ByVal Value As Single) ' implementation codes here End Set End Property Property length() As Single Implements Dimension.length Get ' implementation codes here End Get Set(ByVal Value As Single) ' implementation codes here End Set End Property End Class
Listing 10 also shows that all of the methods and properties defined by the two interfaces are defined by the
SportsCar class. Unlike class inheritance, multiple inheritance in interfaces is allowed, like the following:
Interface Truck Inherits Car Inherits Dimension End Interface
The .NET framework uses a dot syntax to group class libraries into easily recognized groups. This naming convention is known as a namespace. For example, the
System.Xml.Xsl namespace contains classes that perform XSL transformation. Likewise, in your application you may have many classes, each performing different functions. You may run into name collision, where you have one or more classes with the same name. In this case, you can organize your classes using
Namespace Graph Public Class Point ' Members declaration here End Class End Namespace Namespace Direction Public Class Point ' Members declaration here End Class End Namespace
To use the classes with namespaces, you should specify the full namespace:
Dim ptX As New Graph.Point() Dim goNorth As New Direction.Point()
Note: you can also reduce the amount of typing by using the
Imports MyApplication.Graph Dim ptX As New Point()
I will talk more about namespaces in a future article.
In Visual Basic 6.0, object assignment requires the use of the
Set keyword. This has created some confusion for developers who sometimes are just not sure whether to use the
' Visual Basic 6.0 Dim cmdButton As CommandButton Set cmdButton = Command1
In VB.NET, Microsoft has removed this confusion, and the
Set keyword is no longer supported (do not confuse this
Set with the one in
' Visual Basic .NET Dim cmdButton As Button cmdButton = Button1
I hope you now have a good grounding in OO concepts in VB.NET. The best way to learn OO concepts is through examples and some experimentation. Go ahead, try it out yourself, and you will very soon discover that you have learnt much more than you have expected.
Wei-Meng Lee (Microsoft MVP) http://weimenglee.blogspot.com is a technologist and founder of Developer Learning Solutions http://www.developerlearningsolutions.com, a technology company specializing in hands-on training on the latest Microsoft technologies.
Return to ONDotnet.com
Copyright © 2009 O'Reilly Media, Inc.