In .NET programming, an interface is like a contract. It defines the public properties, methods, and events that a class must provide to satisfy the contract. It doesn't indicate how the class must provide these features, however. That's left up to the class's code. It only defines an interface that the class must show to the rest of the world.
In this lesson, you learn how to implement interfaces that are predefined by .NET namespaces. You also learn how to define your own interfaces to make your code safer and more efficient.
The following sections discuss two of the most important advantages provided by interfaces: multiple inheritance and code generalization.
Suppose you define a Vehicle
class with properties such as NumberOfPassengers, MilesPerGallon
, and NumberOfCupHolders
. From this class you can derive other classes such as Car, PickupTruck
, and Bicycle
.
Suppose you also define a Domicile
class that has properties such as SquareFeet, NumberOfBedrooms
, and NumberOfBathrooms
. From this class you can derive Apartment, Condo
, and VacationHome
.
Next you might like to derive the MotorHome
class from both Vehicle
and Domicile
so it has the properties and methods of both parent classes. Unfortunately, you can't do that in Visual Basic. In Visual Basic a class can inherit from only a single parent class.
Though a class can have only one parent, it can implement any number of interfaces. For example, if you turn the Domicile
class into the IDomicile
interface, the MotorHome
class can inherit from Vehicle
and implement IDomicile
. The interface doesn't provide the code needed to implement such IDomicile
features as the HasAnnoyingNeighbor
property, but at least it defines that property so code that uses a MotorHome
object knows the property is available.
To make recognizing interface names easy, you should begin them with I
as in IDomicile, IComparable
, and IWhatever
.
Defining the property but not implementing it might not seem like a big deal, but it enables your code to treat all IDomicile
objects in a uniform way. Instead of writing separate functions to work with Duplex, RusticCabin
, and HouseBoat
objects, you can write a single function that manipulates objects that implement IDomicile
.
That brings us to the second big advantage provided by interfaces: code generalization.
Interfaces can make your code more general while still providing type checking. They enable you to treat objects that have common features as if they were of the interface type, rather than their true individual types.
For example, suppose you write the following method that displays an array of strings in a ListBox
:
Private Sub DisplayValues(ByVal items() As String) lstItems.Items.Clear() For Each item As String In items lstItems.Items.Add(item) Next item End Sub
This function works reasonably well, but suppose you later decide that you need to display the items in a List(Of String)
instead of an array. You could write a new version of the function that was nearly identical to this one but that works with a list instead of an array, as shown in the following code:
Private Sub DisplayValues(ByVal items As List(Of String)) lstItems.Items.Clear() For Each item As String In items lstItems.Items.Add(item) Next item End Sub
If you compare these two functions you'll see that they are practically identical, so if you use them you must write, debug, and maintain two pieces of code that do almost exactly the same thing.
This is where interfaces can help.
Look again at the two functions. They differ only in their parameter definitions; the rest of their code is the same. The functions don't care whether the parameters are arrays or lists. All they really care about is that a For Each
loop can be used to iterate through them.
The predefined IList(Of ...)
interface requires that a class provide various list-like features such as Clear, Add
, and RemoveAt
methods. It also requires support for For Each
. This is a generic interface, so you must provide a type parameter for it to indicate the type of items over which the interface can loop.
Both String()
and List(Of String)
implement IList(Of String)
, so you can combine and generalize the functions by making their list parameter have the type IList(Of String)
instead of String()
or List(Of String)
.
The following code shows the new version of the method. This version can display the items in a String(), List(Of String)
, or any other object that implements IList(Of String)
:
Private Sub DisplayValues(ByVal items As IList(Of String)) lstItems.Items.Clear() For Each item As String In items lstItems.Items.Add(item) Next item End Sub
To make a class that implements an interface, add an Implements
statement after the Class
statement and any Inherits
statements. For example, the following code shows the declaration for a Manager
class that inherits from Person
and implements IComparable
:
Public Class Manager Inherits Person Implements IComparable ... End Class
The only other thing you need to do is implement the properties, methods, and events defined by the interface. The IComparable
interface defines a CompareTo
function that takes an object as a parameter and returns an integer that is less than, equal to, or greater than zero to indicate whether the object should be considered less than, equal to, or greater than the parameter.
For example, the following code shows a Person
class that defines FirstName
and LastName
properties. It implements IComparable(Of Person)
to order Person
objects according to their last names first.
Public Class Person Implements IComparable(Of Person) Public Property FirstName As String Public Property LastName As String
' Compare this Person to another Person. Public Function CompareTo( ByVal other As Person) As Integer Implements System. IComparable(Of Person).CompareTo ' If our last name comes first, we come first. If (LastName.CompareTo(other.LastName) < 0) Then Return −1 ' If our last name comes second, we come second. If (LastName.CompareTo(other.LastName) > 0) Then Return 1 ' If our last names are the same, compare first names. Return FirstName.CompareTo(other.FirstName) End Function End Class
If the current object's LastName
alphabetically precedes the other object's LastName
, the function returns −1
. If the current object's LastName
alphabetically follows the other object's LastName
, the function returns 1
.
If the two objects' LastNames
are the same, the function compares their FirstNames
and returns the result.
Visual Studio is very helpful in writing methods to implement interfaces. When you type the Implements
statement and press [Enter], Visual Studio generates empty properties, methods, and events to implement the interface. All you need to do is fill in their code. For example, if you type Implements IComparable(Of Person)
and press [Enter], Visual Studio generates the following code (I added the line break to make the code fit in the book):
Public Function CompareTo(ByVal other As Person) As Integer _ Implements System.IComparable(Of Person).CompareTo End Function
Now you just need to fill in the code.
There are several ways you can learn more about what an interface is for and what it does. First, you can search the online help. Second, you can right-click on the interface's name in the Code Editor and select Go To Definition to see information, as shown in Figure 29-1.
Finally, you can open the Object Browser (use the View menu's Object Browser command) and search for the interface's name to see a display similar to the one provided by the Go To Definition command (refer to Figure 29-1).
The examples in the preceding sections implement predefined interfaces. This section explains how you can define your own.
Defining an interface is a lot like defining a class with two main differences:
The following code shows a simple IDrawable
interface:
Public Interface IDrawable Property X As Integer Property Y As Integer Property Background As Brush Property Foreground As Pen Sub Draw(ByVal gr As Graphics) End Interface
A class that implements IDrawable
must provide X, Y, Background
, and Foreground
properties, and a Draw
subroutine.
The declarations for the properties look like they are providing a default implementation for them, but they actually only define the required accessors. A class that implements IDrawable
must still provide its own implementations, although it can use auto-implemented properties. For example, the following code shows how the DrawableCircle
class implements its X
property:
Public Property X As Integer Implements IDrawable.X
In this example, you might be better off using true inheritance instead of an interface. If you make a Drawable
class that implements the X, Y, Background
, and Foreground
properties, other classes such as DrawableCircle
could inherit them. In this example an interface makes sense only if the classes already inherit from some other class so they cannot also inherit from Drawable
.
In this Try It, you build the Vehicle
class and the IDomicile
interface described earlier in this lesson. You then make a MotorHome
class that inherits from the first and implements the second. Finally, you create an instance of the derived class.
You can download the code and resources for this Try It from the book's web page at www.wrox.com
or www.vb-helper.com/24hourvb.html
. You can find them in the Lesson29 folder of the download.
In this lesson:
Start a new project. Create a Vehicle
class with the properties NumberOfPassengers, MilesPerGallon
, and NumberOfCupHolders
. Give it a constructor to make it easy to initialize a new object's properties. Override its ToString
method so it returns the object's property values separated by newlines.
Make an IDomicile
interface that defines the properties SquareFeet, NumberOfBedrooms
, and NumberOfBathrooms
. Also make it define a ToString
function that returns a string.
Derive the MotorHome
class from Vehicle
, making it implement IDomicile
. Give it a constructor to make it easy to initialize a new object's properties. Override its ToString
method so it returns the object's property values separated by newlines.
Create an instance of the MotorHome
class. Then use its ToString
method to display its properties in a textbox.
Don't forget to make the MotorHome
class's constructor invoke the base class's constructor. If you don't remember how, see the section "Invoking Other Constructors" in Lesson 26.
Save a little work by making the MotorHome
class's ToString
method call the Vehicle
class's version.
Start a new project. Create a Vehicle
class with the properties NumberOfPassengers, MilesPerGallon
, and NumberOfCupHolders
. Give it a constructor to make it easy to initialize a new object's properties. Override its ToString
method so it returns the object's property values separated by newlines.
Use code similar to the following:
Public Class Vehicle Public Property NumberOfPassengers As Integer Public Property MilesPerGallon As Single Public Property NumberOfCupHolders As Integer Public Sub New(ByVal newNumberOfPassengers As Integer, ByVal newMilesPerGallon As Single,
ByVal newNumberOfCupHolders As Integer) NumberOfPassengers = newNumberOfPassengers MilesPerGallon = newMilesPerGallon NumberOfCupHolders = newNumberOfCupHolders End Sub Public Overrides Function ToString() As String Return "# Passengers: " & NumberOfPassengers & vbCrLf & "MPG: " & MilesPerGallon & vbCrLf & "# Cupholders: " & NumberOfCupHolders End Function End Class
Make an IDomicile
interface that defines the properties SquareFeet, NumberOfBedrooms
, and NumberOfBathrooms
. Also make it define a ToString
function that returns a string.
Use code similar to the following:
Public Interface IDomicile Property SquareFeet As Integer Property NumberOfBedrooms As Integer Property NumberOfBathrooms As Single Function ToString() As String End Interface
Derive the MotorHome
class from Vehicle
, making it implement IDomicile
. Give it a constructor to make it easy to initialize a new object's properties. Override its ToString
method so it returns the object's property values separated by newlines.
Use code similar to the following:
Public Class MotorHome Inherits Vehicle Implements IDomicile Public Property NumberOfBathrooms As Single _ Implements IDomicile.NumberOfBathrooms Public Property NumberOfBedrooms As Integer _ Implements IDomicile.NumberOfBedrooms Public Property SquareFeet As Integer _ Implements IDomicile.SquareFeet Public Sub New(ByVal newNumberOfPassengers As Integer, ByVal newMilesPerGallon As Single, ByVal newNumberOfCupHolders As Integer, ByVal newNumberOfBathrooms As Single, ByVal newNumberOfBedrooms As Integer, ByVal newSquareFeet As Integer) MyBase.New(newNumberOfPassengers, newMilesPerGallon, newNumberOfCupHolders) NumberOfBathrooms = newNumberOfBathrooms
NumberOfBedrooms = newNumberOfBedrooms SquareFeet = newSquareFeet End Sub Public Overrides Function ToString() As String _ Implements IDomicile.ToString Return MyBase.ToString() & vbCrLf & "# Bathrooms: " & NumberOfBathrooms & vbCrLf & "# Bedrooms: " & NumberOfBedrooms & vbCrLf & "Square Feet: " & SquareFeet End Function End Class
Create an instance of the MotorHome
class. Then use its ToString
method to display its properties in a textbox.
The following code creates an instance of the MotorHome
class and displays its properties in resultTextBox
:
' Make a MotorHome and display its properties. Private Sub Form1_Load() Handles MyBase.Load ' Make a MotorHome. Dim myMotorHome As New MotorHome(6, 8.25, 32, 0.5, 3, 150) ' Display its properties. txtMotorHome.Text = myMotorHome.ToString() End Sub
Please select Lesson 29 on the DVD to view the video that accompanies this lesson.
Build a program that defines the IDrawable
interface described earlier in this lesson. Make the DrawableCircle
and DrawableRectangle
classes implement the interface. Hints: Give DrawableCircle
an additional Radius
property and give DrawableRectangle
additional Width
and Height
properties. (For bonus points, make a DrawableStar
class that has a NumberOfPoints
property and draws a star with that number of points.)
An array's Sort
method can take as a parameter an object that implements the generic IComparer
interface. Because this interface is generic, you can tell it what kinds of objects the class can compare. For example, IComparer(Of Car)
means the class can compare Car
objects.
Build a Car
class with the properties Name, MaxSpeed, Horsepower
, and Price
. Override the ToString
method to display the object's properties formatted with fixed-column widths so the values for different Cars
in a listbox will line up nicely as shown in Figure 29-2. (The listbox uses the fixed-width font Courier New, so all the letters have the same width.)
Build a CarComparer
class that implements IComparer(Of Car)
. Give it the following SortType
enum:
' Different kinds of sorts. Public Enum SortType ByName ByMaxSpeed ByHorsepower ByPrice End Enum
Next, give CarComparer
a Sort
property that has type SortType
.
Finally, give the CarComparer
a Compare
method to satisfy the IComparer(Of Car)
interface. Use a Select Case
statement to make the function return a value that varies according to the Sort
value. For example, if Sort
is ByPrice
, then compare the two Cars
' prices. Make the function sort the MaxSpeed, Horsepower
, and Price
values in decreasing order.
If you set a ListView
control's ListViewItemSorter
property equal to an object that implements the IComparer
interface, then the ListView
uses that object to sort its rows. To sort the rows, the control calls the object's Compare
method, passing it two ListViewItem
objects. (Unfortunately, the ListView
control's ListViewItemSorter
property is a nongeneric IComparer
, so it works with nonspecific objects
instead of something more concrete like ListViewItems
.)
For this exercise, make a program with a ListView
control similar to the one shown in Figure 29-3. At design time, edit the ListView
's Columns
collection to define the columns. Edit its Items
collection to define the data and set the control's View
property to Details
.
Next, make a ListViewItemComparer
class that implements IComparer
. Give it a ColumnNumber
property that indicates the number of the column in the ListView
that the object should use when sorting.
Finally, give the ListView
a ColumnClick
event handler. The event handler should create a new ListViewItemComparer
object to sort on the clicked column and then set the control's ListViewItemSorter
property to that object.
You can find solutions to this lesson's exercises in the Lesson29 folder inside the download available on the book's web site at www.wrox.com
or www.vb-helper.com/24hourvb.html
.