Chapter 29. Using Interfaces

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.

INTERFACE ADVANTAGES

The following sections discuss two of the most important advantages provided by interfaces: multiple inheritance and code generalization.

Multiple Inheritance

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.

Note

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.

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

IMPLEMENTING INTERFACES

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.

FIGURE 29-1

Figure 29.1. 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).

DEFINING INTERFACES

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:

  • You use the keyword Interface instead of Class in the declaration.

  • You don't provide any code for the properties, methods, and events that you declare in the interface.

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

Note

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.

TRY IT

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.

Note

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.

Lesson Requirements

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.

Hints

  • 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.

Step-by-Step

  • 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.

    1. 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.

    1. 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.

    1. 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.

    1. 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

Note

Please select Lesson 29 on the DVD to view the video that accompanies this lesson.

EXERCISES

  1. 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.)

  2. 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.)

    FIGURE 29-2

    Figure 29.2. FIGURE 29-2

    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.

  3. 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.)

    FIGURE 29-3

    Figure 29.3. FIGURE 29-3

    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.

Note

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.

FIGURE 29-3
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset