A V I S U A L B A S I C . N E T
C R A S H C O U R S E
Object-Oriented Programming (OOP)
As mentioned earlier, Visual Basic did not meet the test as a true object-oriented
language that implements true object-oriented programming as defined by
abstraction, encapsulation, polymorphism, and inheritance. Visual Basic .NET
not only supports inheritance, but also supports a variety of inheritance implementations
including interface, forms, and implementation or polymorphism
inheritance.
Before you continue any further, let’s briefly discuss the four main concepts
of object orientation and the implementation code for each. Each brief discussion
is followed with an example that demonstrates the discussed OOP concept.
Abstraction
Abstraction is the easiest of the OOP concepts to understand and is often something
we implement naturally without realizing it. In short, abstraction is the
implementation of code to mimic the actions and characteristics of any realworld
entity.
The most commonly-used example for describing abstraction is the abstraction
of a person. Imagine that we want to create an object from a class that represents a
person. A person class will need to describe its characteristics through the implementation
of properties. Actions of the person class are performed by methods.
Encapsulation
Encapsulation is the programmatic equivalent of a black box. An actual black
box may have a switch and dials. Inside the box would be the mechanisms to
perform the actions provided by the black box.
We expose properties and methods through abstraction, but we implement
the actual workings of our component through encapsulation. A few encapsulated
actions might include data access, data validation, calculations, adding data
to an array or collection, or calling other methods or other components.
Exposing our components interface while hiding the component’s implementation
code effectively separates interface implementation from our black
box implementation. This separation helps to modularize components to perform
a more specific task while requiring minimal knowledge of how the black
box actually works.
One of the more useful applications of encapsulation is in making a complex
component. For example, your program may require interaction with a
third party system, but interaction with this system can only be achieved
through a complex API. Rather than requiring all developers on a project to
spend valuable time figuring out how to correctly use the third party API or
even find ways to misuse it, one developer could study the API then encapsulate
it in a component that exposes a less complex interface. This is a common practice
that saves time and reduces potential bugs.
Like abstraction, encapsulation isn’t as much a technology as it is a method
of code implementation. In the case of encapsulation, our method of implementation
separates the exposed interface from the actual implementation code.
Polymorphism
Abstraction is an interface implemented to represent a real-world object; encapsulation
is the implementation of a black box through interface and implementation
separation; and polymorphism is the ability to implement the interface of another
class into multiple classes or to implement multiple interfaces on a single class.
This method of implementation is referred to as interface-based programming.
A vehicle is a good example of polymorphism. A vehicle interface would
only have those properties and methods that all vehicles have, a few of which
might include paint color, number of doors, accelerator, and ignition. These
properties and methods would apply to all types of vehicles including cars,
trucks, and semi-trucks.
Polymorphism will not implement code behind the vehicle’s properties and
methods. (That’s the job of inheritance covered in the next section.) Instead,
polymorphism is the implementation of an interface. If the car, truck, and semitruck
all implement the same vehicle interface, then the client code for all three
classes can be exactly the same.
Implementing the vehicle interface only requires the declaration of properties and methods. To create a new interface, use t he Interface keyword in place of the Class keyword. The client implementing the new interface can do so by using the Implements keyword as shown in the example:
Implements Ivehicle
After using the Implements keyword, you will notice that Intellisense displays the properties and methods of the IVehicle interface. Using the Implements keyword will only give access to the properties and methods of the IVehicle interface; however, you must provide your own code behind the methods and property declarations to match the interface.
Inheritance
Inheritance is the ability to apply another class’s interface and code to your own
class. Remember, with polymorphism, you got the interface; however, you must
apply your own code. The power of inheritance is the ability to inherit code, saving
developers time. This type of inheritance is called implementation inheritance.
To inherit another class, use the Inherits keyword.
Visual inheritance is the ability to inherit another form’s look and feel onto
another. Remember, everything in .NET is a class, including forms. If you create
a project that exists in the MyApp namespace, create a form name MyBaseForm.
The following code will inherit the MyBaseForm within our new form:
Public Class MyNewForm Inherits MyApp.MyBaseForm End Class
Properties
Properties are part of a program’s interface and describe the characteristics of a
class. These properties hold information about a class or, when loaded into
memory, an object. Properties, as they exist in classes, are often referred to as
“data.” When a reference is made to a class’s data, you will know that the reference
is actually directed toward a class’s property.
To create a property, use t he Property keyword and t hen define the type of
property you are implementing. Properties can be read-only, write-only, or read
and writable. To define the characteristics of properties, use t he keywords
ReadOnly, WriteOnly, or supply no definition at all to implement both read and
write ability.
Visual Studio .NET makes properties easier to implement by adding the
basic shell of property code based on the property’s scope definition. Unlike
Visual Basic, Visual Basic .NET automatically supplies code for both read and
write functionality: “Get” for read ability method and “Set” for write access to a
property.
Create a new class, type the following code, and press ENTER:
Public Property FName() As String
Visual Studio .NET will automatically fill in the rest of the code that is
required by the FName property:
Get Return m_FName End Get Set(ByVal Value As String) m_FName = Value End Set End Property
Methods
Methods are the actions exposed by a class in the form of either functions or
sub-procedures. Sub-procedures and functions both execute code on behalf of
the calling application, but sub-procedures simply execute code while functions
execute code, then return a value.
The .NET Framework provides at least two new changes to how you can use
procedures. In Visual Basic, you could call a procedure without the use of
parameters, including procedures that required no parameters at all. The .NET
Framework requires parenthesis to follow all methods even when parameters are
not required. For example:
- Visual Basic 6 method call:
intResult = DoSomething
- Visual Basic .NET method call:
intResult = DoSomething()
Another change is the addition of the Return keyword. When returning a
value for a function in Visual Basic, you set the function’s name equal to the
value being returned. With Visual Basic .NET, you can se t the keyword Return
equal to a value and the value will be returned with the function. This is very useful
when making code more generic. For instance, you can easily cut and past a
method’s code without regard to another method’s function name because the
keyword Return is used for setting the method equal to a return value. Examples
of the old versus new method for returning values of a function are:
- Visual Basic 6 method call:
Public Function DoSomething() as Int32 DoSomething = 10 End Function
- Visual Basic .NET method call:
Public Function DoSomething() as Int32 Return = 10 End Function
- Private: The Private keyword defines a variable or method as accessible
only by code within the context of where the declaration occurred; outside
code is not permitted access. Variables and methods defined as private are
often referred to as member variables or methods, and commonly prefixed
with an “m”. - Public: The Public keyword declares a property or method as accessible by
anyone within the calling application or within the class itself. - Friend: The Friend keyword defines a property or method as accessible by
members within the class it is declared in. - Protected: The Protected keyword defines a property or method as
accessible only by members of its class or by members of an inheriting class. - Default: A Default property is a single property of a class that can be set as the
default. This allows developers that use your class to work more easily with
your default property because they do not need to make a direct reference to
the property. Default properties cannot be initialized as Shared or Private and
all must be accepted at least on argument or parameter. Default properties do
not promote good code readability, so use this option sparingly. - Overloads:The Overloads property allows a function to be described using
deferent combinations of parameters. Each combination is considered a
signature, thereby uniquely defining an instance of the method being
defined. You can define a function with multiple signatures without using
the keyword Overloads, but if you use the Overloads keyword in one, you
must use it in all of the function’s Overloaded signatures. - Shared:The Shared keyword is used in an inherited or base class to define
a property or method as being shared among all instances of a given class.
If multiple instances of a class with shared properties or methods are
loaded, the shared properties or methods will provide the same data across
each instance of the class. When one class alters the value for a shared
property, all instances of that class will reflect the change. Shared properties
of all instances of the class point to the same memory location. - Overridable:The Overridable keyword is used when defining a property or
method of an inherited class, as overridable by the inheriting class. - Overides: The Overides keyword allows the inheriting class to disregard the
property or method of the inherited class and implements its own code. - NotOverridable: The NotOverridable keyword explicitly declares a property
or method as not overridable by an inheriting class, and all properties are
“not overridable” by default. The only real advantage to using this keyword
is to make your code more readable. - MustOverride: The MustOverride keyword forces the inheriting class to
implement its own code for the property or method. - Shadows: The Shadows keyword works like the Overloads keyword except
that with shadows we do not have to follow rules such as implementing the
same signature. The Shadows keyword does not require the consent
(override ability) of the inherited class to replace the property or method’s
implementation code. A method does not have to be defined as overridable
for the Shadows keyword to work.
If you look closely at a function’s supporting properties you will find that
the Return keyword is used by default. You can set the function name equal to a
given value.
The third significant change is in how parameters are passed. Visual Basic
passed a parameter value ByRef by default. The preferred method for passing
parameter values is to explicitly define whether a value is passed by ByRef or
ByVal. Finally, when using the Option keyword, you must define a default value
similar to how C has worked for many years now.
Declaration Options
We have covered a few of the most common declaration methods including
those that describe the scope of a property or method. Several description
options will extend or restrict scope.
Here is a list of the most commonly-used declaration options with brief
descriptions of each:
Object Instantiation
When you drag and drop controls onto a Windows Form, you are using objects.
When you observe your code, you are looking at a class; when that code is
loaded into memory, at runtime, it is considered an object. The importance of
the distinction is simply to describe that a class is a template, while an object is
an instance of that template in memory. Also, many copies of the template can
exist in memory at the same time as objects.
Fortunately, we do not have to depend on the component designer to work
with classes; we can build our own classes and components. This is nothing new
for a moderately experienced developer; what is new is how Visual Basic .NET
permits us to instantiate classes.
Classic COM relied on the Windows Registry to store its exposed properties,
methods, events, and enumerations; a client application could only access
these exposed interfaces through the Registry. As a result, the way you instantiate
classes when using classic COM components in COM+ is very important.
Visual Basic .NET accepts a number of instantiation methods without performance
impacts, although all variables must first be diminished and then instantiated
before they can be used.
The two methods for instantiating classic COM are the CreateObject and
New keywords. CreateObject uses the Windows Registry to obtain the interface
of the class being instantiated. Because CreateObject depends on windows for
access to the register, COM+ can apply a context for use by the COM+ services.
The New keyword in classic COM also depends on Windows for access to the
Windows Registry. The catch is that it doesn’t always have to access the Windows
Registry to discover a class’s interface if the class resides inside the same component
as the calling class. Because the New keyword has no problem accessing a
class’s interface within the same component, a class can be instantiated by passing
COM+ services that would normally add a context or other component service.
While this will not prevent you from loading a class into COM+, to take full advantage
of COM+ services you should use the CreateObject keyword.
Having said all that, the CreateObject keyword cannot be used to instantiate
.NET classes, although it can be used to instantiate classes that exist within classic
COM components. Because .NET components don’t rely on the Windows
Registry, the New keyword is used when loading all .NET components.
Here are several examples of how you might define and load classes into
memory. First, the variable is diminished as MyClass:
Dim obj As MyClass
Second, you load the class “MyClass” into memory. An instance of a class
loaded into memory is referred to as an object. Notice there is no “Set” keyword
used.
Obj = New MyClass
Another method is to declare and instanciate an object in a single line:
Dim obj2 As MyClass = New MyClass()
Finally, you can implicitly diminish a variable with a class you are attempting
to load. This is the shortest method and is perfectly acceptable:
Dim obj3 As New MyClass
Early and Late Binding
Binding is something we do when diminishing a variable, though many developers
may not realize the importance of how they bind a class.
Early binding, often referred to as strong typing, refers to explicitly declaring
the class used to define a variable. Early binding has several benefits. For example,
when programming, Visual Studio .NET can give access to the class’s interface
with Intellisense which greatly reduces potential for typos and promotes
rapid development. Also, when early binding a class, the Visual Basic compiler
can enforce the proper use of a class’s interface by providing warnings and
refusing to complete the compile until the error is resolved. But performance
gains are probably the most important reason to bind early: Early binding allows
your program to access your class’s interface directly, rather than through the
Windows Registry or at runtime. If the compiler knows ahead of time which
classes you will be using in your application, it can make the appropriate compilation
optimizations.
Late binding can be useful when developing against non-existent components
or ones that are being developed. Late binding allows you to continue
compiling your code until the component is available; once the class is available,
you can modify your code to early bind. You might also use late binding when
you truly don’t know the object type that will be passed to your function, in
which case it is perfectly acceptable to accept any type of object.
Before late binding can occur, the Option Strict option must be set to off.
Option strict is off by default:
Option Strict Off
To declare a variable as lat e bound, simply diminish t he variable as type
object: Option Strict Off Dim obj As Object 'or Dim obj As System.Object
The System.Object class is the class from which all other classes are derived.
While it has no specific characteristics that prevent it from acting as any other
class, it is used for late binding.
Components
A class defines something that can exist in memory. It defines an object’s interface
including properties, methods, events, and enumerations as well as implementation
code. An object is an instance of a class in memory; while a class may
exist only once, multiple instances of that class may reside in memory as objects.
When adding items to a project, you can add a “class” or a “component
class”. In essence, these are the same thing with one exception: a component
class implements the IComponent interface, enabling Visual Studio .NET to
drag non-visual controls, such as the timer control, onto a component designer.
Visual Studio .NET provides a designer for building components, which
allows you to drag visual controls onto your class or component, and begin coding.
For example, if you want to program a delay into your class, you can use a
component item with the designer to drag the timer control onto your component.
The implementation of a graphical designer (IE Component Designer)
and container are available to you when you selected “component class” as a
new item. Visual Studio creates your class by adding a line of code that inherits
everything your class needs to be a component, as follows:
Inherits System.ComponentModel.Component
Then the designer creates a components object using the IContainer interface
so that the designer can allow drag and drop capabilities:
Private components As System.ComponentModel.Icontainer
Simple OOP Examples
Now you’ll build an example application that employs abstraction, encapsulation,
polymorphism, and inheritance:
1. Create a new class library project named “PersonProj”.
2. Rename the Class1.vb file to “Person.vb”.
3. Add a new Windows Application project to the solution named “TestClient”.
4. Right- click on t he TestClient project and select Se t as StartUp Project.
Adding Abstraction
Now add abstraction:
1. Replace the default class in Person.vb with the following code:
Public Class clsPerson Public FName As String Public LName As String Public FullName As String Public BirthDate As Date Public Age As Integer Public TotalHours As Integer Public Sub Work(ByVal intHours As Integer) TotalHours += intHours End Sub End Class
2. Create a form that looks like Figure 7-2, and name it “frmAbstraction.vb.”
Use the parameters in Table 7- 4 to build the new Windows Form.
Table 7-4: Form Parameters Control Property Value Button Name btnSubmit Text Submit Textbox Name txtFName Textbox Name txtLName Textbox Name txtFullName Textbox Name txtBirthDate Textbox Name txtAge Textbox Name txtHoursWorked Label Name lblTotalHoursWorked |
3. Right- click on t he TestClient project and select Properties. Change the Startup object to “frmAbstraction.vb”.
4. Add a r eference to the PersonProj. Right – click on t he TestClient project and select Add Reference. Select the Projects tab and press the “Select” button then press OK.
5. Add the following object declaration to the initialization of frmAbstraction form:
' Here we are initializing the Person class. Normally this would be done ' when the class was needed for data access but in this case we are using ' the Person class to maintain our data. Dim objPerson As New PersonProj.clsPerson()
6. Add the following code to the submit buttons click event:
objPerson.FName = txtFName.Text objPerson.LName = txtLName.Text objPerson.FullName = txtFullName.Text If IsDate(txtBirthDate.Text) Then objPerson.BirthDate = CDate(txtBirthDate.Text) End If If IsNumeric(txtAge.Text) Then objPerson.Age = CInt(txtAge.Text) End If If IsNumeric(txtHoursWorked.Text) Then objPerson.Work(CInt(txtHoursWorked.Text)) End If lblTotalHoursWorked.Text = "Total Hours Worked: " _ & objPerson.TotalHours.ToString
7. Now run the application.
You should be able to place any value you wish into the First and Last name
fields, then completely contradict yourself when filling in your full name. The
same should hold true for entering your birth date and age. This example
abstracts a person but does not hide any implementation; each time you press
the Submit butt on, the Total Hours Worked is summed and displayed.
Adding Encapsulation
The encapsulation example implements the clsPerson class and encapsulated
code, hiding the implementation code for all the properties and methods.
In the abstraction example, the user has to enter both their birthday and
age. As you encapsulate the implementation for the Person object, you will hide
the implementation of their age. Age will be derived from the person’s birth
date, thus preventing a user from creating an invalid age and birth date values.
Properties are also encapsulated, allowing the class to derive the full name from
the first and last names that have been entered.
1. Add a new Windows Form item named “frmEncapsulation.vb”.
2. Add the controls and parameters listed in Table 7- 5 to the frmEncapsulation.vb form.
Table 7-5: Controls for the Form Control Property Value Button Name btnSubmit Text Submit Textbox Name txtFName Textbox Name txtLName Label Name lblFullNameDisplay Textbox Name txtBirthDate Label Name lblAgeDisplay Textbox Name txtHoursWorked Label Name lblTotalHoursWorked |
3. Create a new class in the Person class using the following code:
Public Class clsPerson2 Private m_FName As String Private m_LName As String Private m_BirthDate As Date Private m_TotalHours As Integer Public Property FName() As String Get Return m_FName End Get Set(ByVal Value As String) m_FName = Value End Set End Property Public Property LName() As String Get Return m_LName End Get Set(ByVal Value As String) m_LName = Value End Set End Property Public ReadOnly Property FullName() As String Get Return m_FName & " " & m_LName End Get End Property Public Property BirthDate() As Date Get Return m_BirthDate End Get Set(ByVal Value As Date) If IsDate(Value) Then m_BirthDate = Value End If End Set End Property Public ReadOnly Property Age() As Integer Get If DatePart(DateInterval.Year, m_BirthDate) = 1 Then Exit Property End If Return DateDiff(DateInterval.Year, m_BirthDate, Now) End Get End Property 'Method for adding hours to m_TotalHours worked. Public Sub Work(ByVal intHours As Integer) m_TotalHours += intHours End Sub Public ReadOnly Property TotalHoursWorked() As Integer Get Return m_TotalHours End Get End Property End Class
4. Add the following object declaration to the initialization of frmAbstraction form:
' Here we are initializing the Person class. Normally this would be done ' when the class was needed for data access but in this case we are using ' the Person class to maintain our data. Dim objPerson As New PersonProj.clsPerson2()
5. Right- click and select Properties then change the StartUp object to “frmEncapsulation”.
6. Add the following code to the Submit button:
objPerson.FName = txtFName.Text objPerson.LName = txtLName.Text If IsDate(txtBirthDate.Text) Then objPerson.BirthDate = CDate(txtBirthDate.Text) Else MsgBox("Please provide a valid Birth Date.") End If If IsNumeric(txtHoursWorked.Text) Then objPerson.Work(CInt(txtHoursWorked.Text)) txtHoursWorked.Text = "" End If lblFullNameDisplay.Text = objPerson.FullName.ToString lblAgeDisplay.Text = objPerson.Age.ToString lblTotalHoursWorked.Text = "Total Hours Worked: " _ & objPerson.TotalHoursWorked.ToString
7. Now run the application and enter the information.
You’ll notice that your age is calculated for you so that it cannot contradict
what the age should be based on the birth date, and the full name is derived
from the first and last name.
Adding Polymorphism or Interface-based Inheritance
This example features an interface called IPerson and a class named Employee
that uses the IPerson interface:
1. First create a new Windows Form with the same controls as used in the
encapsulation example and name it “frmPolymorphism”.
2. Add a new Module to the PersonProj project and name it
“MyInterfaces.vb”.
3. Apply the following code to the MyInterface.vb module. The code defines
the interface. Public Interface IPerson Property FName() As String Property LName() As String ReadOnly Property FullName() As String Property BirthDate() As Date ReadOnly Property Age() As Integer 'Method for adding hours to m_TotalHours worked. Sub Work(ByVal intHours As Integer) ReadOnly Property TotalHoursWorked() As Integer End Interface
4. Right- click and select Properties, then change the StartUp object to
“frmPolymorphism”.
5. Create a new class to the Person.vb module. You will use it to inherit the new interface:
Public Class clsPerson3 Implements IPerson Private m_FName As String Private m_LName As String Private m_BirthDate As Date Private m_TotalHours As Integer Private m_HrRate As Integer Private m_TotalPay As Double Public Property FName() As String _ Implements IPerson.FName Get Return m_FName End Get Set(ByVal Value As String) m_FName = Value End Set End Property Public Property LName() As String _ Implements IPerson.LName Get Return m_LName End Get Set(ByVal Value As String) m_LName = Value End Set End Property Public ReadOnly Property FullName() As String _ Implements IPerson.FullName Get Return m_FName & " " & m_LName End Get End Property Public Property BirthDate() As Date _ Implements IPerson.BirthDate Get Return m_BirthDate End Get Set(ByVal Value As Date) If IsDate(Value) Then m_BirthDate = Value End If End Set End Property Public ReadOnly Property Age() As Integer _ Implements IPerson.Age Get If DatePart(DateInterval.Year, _ m_BirthDate) = 1 Then Exit Property Return DateDiff(DateInterval.Year, _ m_BirthDate, Now) End Get End Property 'Method for adding hours to m_TotalHours worked. Public Sub Work(ByVal intHours As Integer) _ Implements IPerson.Work m_TotalHours += intHours End Sub Public ReadOnly Property TotalHoursWorked() As Integer _ Implements IPerson.TotalHoursWorked Get Return m_TotalHours End Get End Property 'Additional Properties: 'HrRate Public WriteOnly Property HrRate() Set(ByVal Value) m_HrRate = Value End Set End Property 'TotalPay Public ReadOnly Property TotalPay() As Double Get Return m_HrRate * m_TotalHours End Get End Property End Class
6. Now run this example just as you ran the previous ones.
You will not notice a difference in how the application runs, although the
plumbing has changed quite a bit.
With this simple example, it is easy to question the usefulness of polymorphism.
However, if you were to continue building an application that dealt with
several aspects of a person, you might find polymorphism more helpful to use if
you had to deal with Employees, Customers, Managers, and Contractors.
Working with Inheritance
This inheritance example inherits the interface and implementation code of the
clsPerson3 class:
1. Add a new Windows Form item named “frmInheritance.vb”. Use the same
controls as we used in the encapsulation example.
2. Add the following code to the initialization section of the form:
' Here we are initializing the Person class. Normally this would be done ' when the class was needed for data access but in this case we are using ' the Person class to maintain our data. Dim objPerson As New Person.clsEmployee()
3. Add the following class to the Person.vb module of the PersonProj project:
Public Class clsEmployee Inherits clsPerson3 End Class
4. Right- click and select Properties, then change the StartUp object to
“frmInheritance”.
5. Now Run the inheritance example as you did the previous ones.
Notice that the clsEmployee class inherits the functionality of the clsPerson3
class, which in turn implements the IPerson interface. This example demonstrates
both polymorphism and inheritance that have been combined to form a
single solution.
Summary
In this chapter, you learned that Visual Basic has come a long way from a
reduced featured-set language that promoted RAPID application development
to a full featured language. Now employing full inheritance, Visual Basic promises
to aid in the delivery of enterprise level applications that may previously
have been better delivered in another OOP language.
# # #