You can access the members (i.e., methods and properties) of an interface through the object of any class that implements the interface. Thus, you can access the methods and properties of IStorable through the Document object, as if they were members of the Document class:
Dim doc As New Document("Test Document") doc.Status = -1 doc.Read( )
Alternatively, you can create an instance of the interface, and then use that interface to access the methods of that interface:
Dim isDoc As IStorable = doc isDoc.status = 0 isDoc.Read( )
In Chapter 9, you'll see that at times you may create collections of objects that implement a given interface (e.g., a collection of storable objects). You can manipulate them without knowing their real type—so long as they implement IStorable. For instance, you won't know that you have a Document object; rather, you'll know only that the object in question implements IStorable. You can create a variable of type IStorable and cast your Document to that type. You can then access the IStorable methods through the IStorable variable.
When you cast you say to the compiler, "Trust me, I know this object is really of this type." In this case you are saying, "Trust me, I know this document really implements IStorable, so you can treat it as an IStorable."
As stated earlier, you cannot instantiate an interface directly—that is, you cannot write:
IStorable isDoc As New IStorable( )
You can, however, create an instance of the implementing class, and then create an instance of the interface:
Dim isDoc As IStorable = doc
(isDoc is a reference to an IStorable object.) This is considered a widening conversion (from Document to the IStorable interface), and so there is no need for an explicit cast.
In general, it is a better design decision to access the interface methods through an interface reference. Thus, it was better to use isDoc.Read( ), than doc.Read( ), in the previous example. Access through an interface allows you to treat the interface polymorphically. In other words, you can have two or more classes implement the interface, and then by accessing these classes only through the interface, you can ignore their real runtime type and treat them simply as instances of the interface. You'll see the power of this technique in Chapter 3.
There may be instances in which you do not know in advance (at compile time) that an object supports a particular interface. For instance, given a collection of objects, you might not know whether each object in the collection implements IStorable, ICompressible, or both.
You can find out what interfaces are implemented by a particular object by casting blindly and then catching the exceptions that arise when you've tried to cast the object to an interface it hasn't implemented. The code to cast Document to ICompressible might be:
Dim icDoc As ICompressible = doc icDoc.Compress( )
If it turns out that if Document implements only the IStorable interface but not the ICompressible interface:
Public Class Document Implements IStorable
the cast to ICompressible will fail to compile (assuming Option Strict is On as it should be). If you turn Option Strict off, the code will compile, but at runtime, because of the illegal cast, the program will throw an exception:
System.InvalidCastException: Specified cast is not valid.
|
You could then catch the exception and take corrective action, but this approach is ugly and evil and you should not do things this way. This is like testing whether a gun is loaded by firing it; it's dangerous and it annoys the neighbors.
Rather than firing blindly, you would like to be able to ask the object if it implements an interface, in order to then invoke the appropriate methods; to do so you will use the Is operator.
The TypeOf...Is expression lets you query whether an object implements an interface. The syntax of the expression is:
TypeOf expression Is type
This expression evaluates true if the tested expression (which must be a reference type, such as an instance of a class) can be safely cast to type (e.g., an interface) without throwing an exception.
Example 8-3 illustrates the use of the TypeOf and Is keywords to test whether a Document object implements the IStorable and ICompressible interfaces.
Option Strict On Imports System Namespace InterfaceDemo Interface IStorable Sub Read( ) Sub Write(ByVal obj As Object) Property Status( ) As Integer End Interface 'IStorable ' here's the new interface Interface ICompressible Sub Compress( ) Sub Decompress( ) End Interface 'ICompressible ' document implements only IStorable Public Class Document Implements IStorable ' the document constructor Public Sub New(ByVal s As String) Console.WriteLine("Creating document with: {0}", s) End Sub 'New ' implement IStorable Public Sub Read( ) Implements IStorable.Read Console.WriteLine("Implementing the Read Method for IStorable") End Sub 'Read Public Sub Write(ByVal o As Object) Implements IStorable.Write Console.WriteLine( _ "Implementing the Write Method for IStorable") End Sub 'Write Public Property Status( ) As Integer Implements IStorable.Status Get Return Status End Get Set(ByVal Value As Integer) Status = Value End Set End Property ' hold the data for IStorable's Status property Private myStatus As Integer = 0 End Class 'Document Class Tester Public Sub Run( ) Dim doc As New Document("Test Document") ' only cast if it is safe If TypeOf doc Is IStorable Then Dim isDoc As IStorable = doc isDoc.Read( ) Else Console.WriteLine("Could not cast to IStorable") End If ' this test will fail If TypeOf doc Is ICompressible Then Dim icDoc As ICompressible = doc icDoc.Compress( ) Else Console.WriteLine("Could not cast to ICompressible") End If End Sub 'Run Shared Sub Main( ) Dim t As New Tester( ) t.Run( ) End Sub 'Main End Class 'Tester End Namespace 'InterfaceDemo Output: Creating document with: Test Document Implementing the Read Method for IStorable Could not cast to ICompressible
In Example 8-3, the Document class implements only IStorable:
Public Class Document Implements IStorable
In the Run( ) method of the Tester class, you create an instance of Document:
Dim doc As New Document("Test Document")
and you test whether that instance is an IStorable (that is, does it implement the IStorable interface?):
If TypeOf doc Is IStorable Then
If so, you create an instance of the IStorable interface and call an interface method (isDoc.Read):
Dim isDoc As IStorable = doc isDoc.Read( )
You then repeat the test with ICompressible, and if the test fails, you print an error message:
If TypeOf doc Is ICompressible Then Dim icDoc As ICompressible = CType(doc, ICompressible) icDoc.Compress( ) Else Console.WriteLine("Could not cast to ICompressible") End If
The output shows that the first test (IStorable) succeeds (as expected) and the second test (of ICompressible) fails, also as expected:
Implementing the Read Method for IStorable Could not cast to ICompressible
Top |