Learning C# and OOP, Polymorphism and Interfaces, Part 1Baldwin provides an introduction to interfaces in C#, and compares them with interfaces in Java. Published: November 10, 2002 C# Programming Notes # 128a PrefaceThis lesson is one of a series of lessons designed to teach you about the essence of Object-Oriented Programming (OOP) using C#. The first lesson in the group was entitled Learning C# and OOP, Getting Started, Objects and Encapsulation. That lesson, and each of the lessons following that one, has provided explanations of certain aspects of Object-Oriented Programming using C#. The previous lesson was entitled Learning C# and OOP, Polymorphism and the Object Class. Necessary and significant aspects This miniseries will describe and discuss the necessary and significant aspects of OOP using C#. If you have a general understanding of computer programming, you should be able to read and understand the lessons in this miniseries, even if you don't have a strong background in the C# programming language. Viewing tip You may find it useful to open another copy of this lesson in a separate browser window. That will make it easier for you to scroll back and forth among the different figures while you are reading about them. Supplementary material I recommend that you also study the other lessons in my extensive collection of online programming tutorials. You will find a consolidated index at www.DickBaldwin.com. PreviewWhat is polymorphism? The meaning of the word polymorphism is something like one name, many forms. How does C# implement polymorphism? Polymorphism manifests itself in C# in the form of multiple methods having the same name. In some cases, multiple methods have the same name, but different formal argument lists (overloaded methods, which were discussed in a previous lesson). In other cases, multiple methods have the same name, same return type, and same formal argument list (overridden methods, which were also discussed in a previous lesson). Three distinct forms of polymorphism From a practical programming viewpoint, polymorphism manifests itself in three distinct forms in C#:
Method overloading I covered method overloading as one form of polymorphism (compile-time polymorphism) in a previous lesson. I also explained automatic type conversion and the use of the cast operator for type conversion in a previous lesson. Method overriding through inheritance I discussed runtime polymorphism implemented through method overriding and class inheritance in previous lessons. Method overriding through the C# interface I will begin a discussion of polymorphism based on method overriding through the C# interface in this lesson. I will continue with that topic for a few more lessons following this one. This lesson will provide background information for interfaces in C#. The next few lessons will provide sample programs that illustrate and expand upon the material contained in this lesson. For those who are interested, I will also make numerous comparisons between C# interfaces and Java interfaces. The purpose is to assist those who are making the transition from Java to C#. DiscussionInterfaces can be difficult to understand I have been teaching Object-Oriented Programming (OOP) to several hundred Java students each year since 1997. (Prior to that time, I taught OOP using C++.) During this period, I have found that many students have difficulty understanding polymorphism based on interfaces.
Because of the inherent difficulty associated with interfaces, I plan to take it slow and easy in explaining the use of interfaces in this and the next few lessons. Let's build a houseboat Let's begin with a hypothetical real-world example. Suppose that you wanted to define a class whose objects represent houseboats. You could, of course, start from scratch and define a new HouseBoat class. A House class and a Boat class However, let's assume that you already have access to a class named House, which describes the attributes of a house (bedroom, kitchen, heating system, cooling system, etc.). Let's also assume that you have access to a class named Boat, which describes the attributes of a boat (motor, hull shape, safety equipment, etc.). A HouseBoat class If you happen to be a C++ programmer, you could define a new class named HouseBoat, which extends or inherits from the class named House, and which also extends or inherits from the class named Boat.
An ideal solution Objects instantiated from your new class would have the attributes of a house, and would also have the attributes of a boat. At the surface, this sounds like an ideal solution. However, many subtle problems arise with the use of multiple inheritance in C++, and it is very easy to make serious programming errors. As a result, neither Java nor C# allow multiple inheritance. (At least not in the same way that multiple inheritance is allowed in C++.) In both Java and C#, a new class is allowed to extend only one other class. One solution to the problem Without getting into details of the reasons why, many of the problems associated with multiple inheritance can be avoided if all but one of the extended classes are pure abstract classes.
While such an inheritance structure may not be quite as powerful as the multiple-inheritance structure described for C++ above, this structure does provide many of the benefits of multiple inheritance and it avoids many of the problems. This is the solution provided by both Java and C#. An interface is a pure abstract class An interface in Java and C# meets the above description of a pure abstract class. According to C# In A Nutshell,
Some jargon As usual, there is some jargon that we must deal with. For example, we don't say that a class extends or inherits from an interface. Rather, we say that a class implements an interface.
Back to our HouseBoat example To handle the previous example in C#, we might have a class named House, and an interface named IBoat. Our new HouseBoat class could extend the House class and implement the IBoat interface.
Will quote several authors In order to understand a new concept, it is often useful to hear what different authors have to say about the concept. To help you to understand the use of interfaces, I will provide quotations from several authors along with my own comments and explanations. Visual C#.NET by Joyce Farrell For example, here is what Joyce Farrell has to say about multiple inheritance in her book entitled Visual C#.NET. Some of what she has to say echoes what I told you above.
What is an interface? I find it very interesting how different authors provide different descriptions of the interface and its purpose. Farrell goes on to tell us,
Interface vs. abstract class Farrell has this to say about the differences between an interface and an abstract class,
The interface definition Farrell goes on to say,
C# In A Nutshell, by Drayton, Albabari, and Neward According to C# In A Nutshell,
Why use an interface? Nutshell also tells us,
Programming C#, by Jesse Liberty According to Programming C#, by Jesse Liberty,
In other words, when a class implements an interface, it tells any potential client,
An alternative to an abstract class Liberty goes on to tell us,
A new type Stated differently, when you define a class, abstract or otherwise, you introduce a new type into the system. Similarly, when you define an interface, you also introduce a new type into the system. Whereas a class definition defines the behavior of an object of the class type, however, an interface definition simply defines the user interface to an object of the interface type, without saying anything about behavior. The behavior of the object is defined by the class that implements the interface. C# Primer, A Practical Approach, by Stanley Lippman Now let's see what Stanley Lippman has to say in his book entitled C# Primer, A Practical Approach. Lippman begins by telling us,
In this paragraph, Lippman hits upon one of the minor differences between interfaces in C# and Java. Java interfaces can contain constants, while C# interfaces cannot contain constants. I will have more to say about this difference later. Definition of a simple interface Figure 1 shows the definition
of a simple interface named I1. This interface declares an
(implicitly) public abstract
virtual method name p, which takes no parameters and returns nothing.
Must define the method named p Any class that implements the interface named I1 must define a method named p, whose signature matches that declared in the interface. May implement p with different behaviors It is extremely important to note, however, that if two or more classes implement the interface named I1, each of those classes may define the method named p to have different behavior. If you then instantiate objects of those different classes and invoke the method named p on each of the objects, the behavior resulting from the invocation of p on one object may be radically different from the behavior resulting from the invocation of p on a different object. Syntax to implement an interface Listing 2 shows the syntax for
causing a class to implement an interface.
If you have been studying the previous lessons, you will recognize that this syntax is indistinguishable from the syntax used to cause a class to extend another class. In particular, it is not possible to examine the first line of a class definition and determine whether the name following the colon is the name of a class, or is the name of an interface.
Extending a class and implementing interfaces Figure
3 shows the syntax for a class
named B, which extends the class named A, and which also implements the interfaces named I1
and I2. Note that the superclass name and the interface names are
separated by commas.
The required order of the referenced class and the interfaces Nutshell tells us,
In other words, if there is a designated superclass (base class), the reference to the superclass must appear in the list of comma-separated references before any of the references to the interfaces. When to define an interface According to Farrell,
Non-abstract methods can be overridden I will make a couple of clarifying comments regarding the above quotation from Farrell. First, it is not necessary for a method to be declared abstract in order for it to be overridden in a subclass (but it is necessary that the method be declared virtual). For example, the Object class defines several virtual methods which are intended to be inherited and used as defined, or to be overridden if their behavior is inappropriate for an object instantiated from the new class. Multiple inheritance not supported I will also mention that because C# doesn't support multiple inheritance, it is often necessary to define and use interfaces in situations where another language (such as C++) might use classes instead. In other words, if your new class must inherit from some class, the only way to achieve the benefits of multiple inheritance in C# is to cause your new class to also implement one or more interfaces. Polymorphic behavior with interfaces Now we have reached my favorite part of this discussion (which is the main topic of this and the next few lessons). Continuing with Farrell,
Nutshell tells us,
Lippman tells us,
A simple example of polymorphic behavior With those three descriptions as background, let me provide a simple example. Assume that you have several objects instantiated from the hypothetical classes named Circle, Rectangle, and Triangle stored in an array as type IShape. Assume also that each of the classes listed above implements the hypothetical interface named IShape, which declares a method named area.
Assume that the purpose of the method named area, as declared in the interface, is to calculate the area of a shape. Invoke the area method on any object in the array Given these circumstances, you could invoke the area method on any of the objects in the array knowing that the calculation of the area will be appropriate for the type of shape involved. In fact, you wouldn't even need to be concerned about the actual classes from which the objects were instantiated. If they all implement IShape, they could all be treated as type IShape. If they all implement IShape correctly, you could be confident that the value returned from the area method would be correct, regardless of the actual shape involved. Contents of an interface definition Lippman tells us,
Figure 4 shows the syntax
for declaring a method, a property, and an indexer in an interface definition.
What is prohibited in an interface? Lippman tells us,
Comparison with a Java interface As mentioned above, a C# interface can contain declarations of properties, events, methods, and indexers. However, it cannot contain member variables or constants. A Java interface can only contain method declarations and constants (final variables). However, because events, properties and indexers (indexed properties) are implemented using methods in Java (unlike C# which requires special syntax for those constructs), a Java interface can contain the same things that can be contained in a C# interface. Java interface can contain constants In addition, a Java interface can contain constants.
However, there are other good ways to handle constants in both languages, so the lack of constants in a C# interface should not be viewed as a serious limitation. A common service for otherwise unrelated types Lippman states,
How does Baldwin explain this? I usually explain this in the following way in the classroom. I first draw an inheritance hierarchy on the chalkboard, and emphasize that from a type viewpoint, an object of a given class is related only to its ancestors (superclasses) in the hierarchy (parent, grandparent, great-grandparent, etc.). For example, an object instantiated from a class can be treated as the type of any of its superclasses, but it cannot be treated as the type of a subclass, a sibling, a cousin, an uncle, etc. A new relationship However, if two or more classes which have absolutely no relationship up and down the family tree in the inheritance hierarchy implement the same interface, an object instantiated from any of those classes can be treated as a new type (which is the name of the interface). Furthermore, any of those object's references can be passed as a parameter to any method that expects in incoming parameter of the interface type, and any of the methods declared in the interface can be invoked those object's references. However, the behavior of a particular method invoked on an object instantiated from one such class may be completely different from the behavior of the same method invoked on an object instantiated from another such class. Interfaces are meant to be mixed in Liberty explains,
When different classes implement the same interface, a new relationship among those classes is formed, which has nothing to do with the class inheritance hierarchy. A form of multiple inheritance Lippman tells us,
Thus, objects instantiated from a class that implements several different interfaces belongs to several different relationships. One set of relationships is based on the superclasses of the class in the normal class inheritance hierarchy. The other relationships are determined by the interfaces implemented by the class. Be aware, however, that whenever an object of that class is treated as the type of one of the interfaces, the only methods that can be invoked on the object are those methods declared in the interface. If you need to invoke methods declared in a different interface, a cast must be used to change the type of the object's reference. The letter and the spirit of the interface Here is another quotation from Lippman that I particularly like.
A simple example Perhaps I can explain what this means by providing a concrete example. Consider the interface in the C# library named ICollection. Objects of many classes, such as Hashtable, Stack, and Queue, (that are used to store collections of object references), implement this interface. The documentation for this interface lists three properties, one of which is Count. The documentation for the property named count reads as follows:
While the documentation for the Count property is very simple, it is also very specific. To begin with, there is the implication that when you define a class that implements ICollection, an object of that class will be capable of storing a collection of object references. Further, when you write the code that implements the Count property, it is incumbent on you to write code that will get and return the number of elements contained in the collection. Can't be forced by the compiler There is no way that the compiler can force you to comply with the instructions in the documentation. For example, as long as your Count method returns an int, the compiler has no way of knowing if the int value returned is correct according to the documented requirements. It is up to you to read and understand the documentation, and to write code that will satisfy the documented requirements. More on the interface members Continuing with Lippman,
In other words, don't attempt to declare the members of the interface either abstract or public, but be aware that when you define an interface method in a class, you are effectively overriding a public abstract virtual method. Multiple inheritance for interfaces
An interface can extend or inherit from one or more other interfaces using the syntax
shown in Figure 5.
The interface named ISample in Figure 5 extends both ICollection and ICloneable. When a class implements an interface, which in turn extends other interfaces, the class must define all the methods that are declared in the implemented interface, plus all the methods inherited into that interface. (I will show you an example of this in a future lesson.) Interfaces inherit from the Object class An interface cannot explicitly inherit from a class or a struct. However, interfaces implicitly inherit from the root class named Object. Among other things, this means that when an object's reference is being treated as an interface type, you can invoke any of the methods defined in the Object class (such as ToString) on that reference. The difference between is-a and implements Liberty discusses the difference between the is-a relationship and the implements relationship. As an example, he explains,
In other words, Liberty is saying that a house and a car would typically have no relationship in a normal class hierarchy. (It is unlikely that a Car class would be a subclass of a House class, and vice versa.) However, they do have a relationship that is independent of the class hierarchy. That relationship is that they are both very expensive, and most people need to take out a big loan to purchase one. Sleazy Jack's Finance Company In the above example the CanBeBoughtWithABigLoan interface might declare a method named makeLoan. Assuming that the Car class and the House class both implement this interface, they would both be required to define the makeLoan method. However, the behavior of the method, as defined in the House class might be radically different from the behavior of the same method as defined in the Car class. For example, the makeLoan method in the House class might indicate the need to visit a mortgage company, to fill out lots of forms, to be very patient waiting for approval, and to make house payments for thirty years. The makeLoan method in the Car class might indicate the need to visit Sleazy Jack's Finance Company, to sign one form, to drive out the same day, and to continue paying for the car for the rest of your life. Using the current jargon, in each case, the behavior of the method would be appropriate for an object instantiated from the class (on second thought, visiting Sleazy Jack might not be an appropriate way to finance a car). The is operator versus the as operator When working with objects as interface types, it is often necessary to cast from one interface type to another. If you attempt a cast that is not possible, an exception will be thrown. Nutshell tells us,
Before casting a reference to an interface type, you might
want to use the is operator to confirm that the object implements the
interface, as shown in Figure 6. In other words, you might want to
confirm in advance that the cast is possible so as to avoid the possibility
of having to deal with an exception.
The code in Figure 6 uses the is operator to confirm that the object referred to by abc implements the interface named IOk. If so, the reference is cast to type IOk, and is stored in the reference variable of type IOk named x. A more efficient approach Liberty tells us that there is a
more efficient approach that makes use of the as operator instead of
the is operator, as shown in
Figure 7.
Again, Nutshell tells us,
The first statement in Listing 7 above attempts to:
If it is not possible to convert the contents of abc to type IOk, the expression to the right of the assignment operator in the first statement will evaluate to null without throwing an exception. The contents of x are then tested for null to determine if the cast was successful. Why is this more efficient? I'm not going to try to explain why the second approach is more efficient than the first. It has to do with how the code is compiled into the intermediate language. You can refer to Liberty's book if you want more information in this regard. However, I did want to let you know that there are two ways to change the type of a reference from one type to another. One way is to use a standard cast. The other way is to use the as operator. Furthermore, according to Liberty, the use of the as operator is the more efficient of the two. Overriding interface methods in a subclass Liberty tells us,
As a result, when a class implements an interface, subclasses of that class can override the methods to provide different implementations. Name collisions Nutshell tells us,
I won't elaborate on this issue. Rather, if you have a problem with name collisions, you know that you can find the solution in Nutshell. SummaryIn this lesson, I have provided an introduction to interfaces in C#, and have compared them with interfaces in Java. What's Next?In the next several lessons, I will explain the use of polymorphism based on method overriding through the C# interface.
Copyright 2002, Richard G. Baldwin. Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited. About the authorRichard Baldwin is a college professor (at Austin Community College in Austin, Texas) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web. Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas. He is the author of Baldwin's Programming Tutorials, which has gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine. Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems. -end- |
© 1996, 1997, 1998, 1999, 2000, 2001, 2002 Richard G. Baldwin |