In last month's column, I introduced the basic object-oriented programming concepts of inheritance. I also discussed a few critical rules that the programming model of the Common Language Runtime (CLR) places on the use of inheritance. If you are unfamiliar with the concepts of inheritance, you should read the November column before diving in. This month, I am going to start by talking about how overridable methods work. As you'll see, overridable methods provide the key to achieving polymorphism with inheritance. However, a full understanding of how these methods work requires you to know the difference between static binding and dynamic binding. Most of the work for static binding occurs at compile time, which improves performance. In addition, static binding is based on the type of the reference variable being used, not on the type of the object. Static binding can produce confusing and inconsistent results when a derived class shadows or hides members from its base class. Once you understand these principles, it's much easier to appreciate why dynamic binding works the way it does. After a quick review of static binding, I will continue with a discussion of dynamic binding and designing base classes with overridable methods. As you will see, overridable methods are the critical mechanism in achieving polymorphism because they allow a derived class author to replace behavior defined in a base class.
One of the most valuable features of inheritance is that the designer of a base class can provide default behavior that can be overridden by a derived class author. For example, if a base class author provides a default implementation for a method, a derived class author can choose whether to use the base class implementation or write a specialized implementation. In some cases, a derived class author can even use the base class implementation and extend it by adding extra code to the derived class. It's important to note that overriding the implementation of a method or a property is very different from shadowing or hiding because it's based on dynamic binding instead of static binding. As you know, static binding relies on the type of the reference variable being used for locating the implementation of the target member. As you saw last month, a client-side programmer can experience inconsistent behavior with static binding when interacting with an object through reference variables based on different types. Dynamic binding is more predictable than static binding because it relies on the type of the actual object that is being used for locating the implementation of the target member. For this reason, designs that involve overriding are easier to use than those that involve shadowing or hiding. A client-side programmer will not experience the same kind of inconsistent behavior when interacting with an object through reference variables based on different types. When a client program is written to interact with an object through static binding, it's easy for the compiler to determine the type of reference variable being used. However, it may be impossible for the compiler to determine what type of object you'll be dealing with at compile-time. For example, imagine you create a module or a class with this method definition:
Sub ProcessTheObject(obj As Class1) obj.Foo(15)End Sub
Sub ProcessTheObject(obj As Class1)
As you know, a client could call the ProcessTheObject method by passing an object created from Class1. The client could also call this method by passing an object created from a class that inherits either directly or indirectly from Class1. When you compile a method with a parameter based on an inheritable class type (such as Class1 in this example) into an EXE or a DLL, the Visual Basic® .NET compiler has no guarantee regarding what type of object will be passed at runtime. This is not an issue specific to Visual Basic .NET; other compilers, such as the one designed for the C# language, have the same problem. Because an object's type cannot always be determined at compile time, the CLR supports a scheme for determining an object's type at runtime and then using this type information to locate the appropriate method or property implementation. Dynamic binding is the fundamental mechanism behind this scheme. Before going any further, you should understand that the features of dynamic binding don't apply to all kinds of class members. When you add methods and properties to the design of a base class, you have the option of defining them to use either static or dynamic binding. However, you don't have the same option when adding fields to a base class. In the CLR, fields can only be accessed through static binding. In other words, method and properties can be declared as overridable, but fields cannot.
Now that you have some understanding of method and property overriding, let's look at a simple example of the syntax required by Visual Basic .NET. The first requirement of overriding is that a base class must define a method that is overridable by using the Overridable keyword.
Class Class1 Overridable Sub Foo() '*** method definition End Sub End Class
Overridable Sub Foo()
'*** method definition
Defining a method with the Overridable keyword in this case means that all access to the Foo method through a reference type of Class1 will result in dynamic binding instead of static binding. It also means that classes that derive from Class1 can override the Foo method to provide a more specialized implementation. Since dynamic binding is slower than static binding, it makes sense that a base class author must ask for it explicitly. However, there's another important reason why languages like Visual Basic .NET and C# require a base class author to be explicit when declaring methods as overridable. When a method is overridable, it complicates the contract between a base class and its derived classes. This will be covered in more detail later. For now, just take it on faith that declaring a method or property as overridable increases your responsibilities as a base class author. By forcing a base class author to use the Overridable keyword, the Visual Basic .NET compiler is forcing you to recognize that overridable methods require more attention during design and programming. You should always design with inheritance in mind. Support for overridable methods and properties should never be added as an afterthought. Now, let's create a derived class that overrides a method implementation defined in its base class. Obviously, the derived class must contain a method with the same name and signature as an overridable method in its base class. Second, the overriding method must be explicitly declared to override the base class implementation using the Overrides keyword.
Class Class1 Overridable Sub Foo() '*** method definition End Sub End ClassClass Class2 : Inherits Class1 Overrides Sub Foo() '*** method definition End Sub End Class
Class Class2 : Inherits Class1
Overrides Sub Foo()
Next, let's review an example of dynamic binding. Examine the following client-side code:
Dim refA As Class2 = New Class2Dim refB As Class1 = refArefA.Foo() '*** calls Class2.FoorefB.Foo() '*** calls Class2.Foo
Dim refA As Class2 = New Class2
Dim refB As Class1 = refA
refA.Foo() '*** calls Class2.Foo
refB.Foo() '*** calls Class2.Foo
As you can see, it doesn't matter whether you access an object created from Class2 through a reference variable of type Class2 or Class1. The dynamic binding scheme employed by the CLR always locates the appropriate method implementation by looking for the most-derived class that holds a definition for the method in question. In this last example, Class2 is the most-derived class that contains an implementation of the Foo method. Keep in mind that it is legal to shadow an overridable method. While this is rarely done, be aware that sloppy syntax can result in shadowing by mistake. For example, what happens when a base class defines an overridable method and a derived class author attempts to override it, but forgets to use the Overrides keyword? The compiler produces a warning, yet compiles your code as if you had used the Shadows keyword.
Class Class1 Overridable Sub Foo() '*** method definition End Sub End ClassClass Class2 : Inherits Class1 Sub Foo() '*** shadows Class1.Foo End Sub End Class
'*** shadows Class1.Foo
When you override a method, it's common to chain a call from your overriding implementation in the derived class to the overridden implementation in the base class. This allows you to take advantage of the implementation provided by the base class and extend it with extra code in the derived class. Take a look at the following code:
Class Class1 Overridable Sub Foo() '*** base class implementation End Sub End ClassClass Class2 : Inherits Class1 Overrides Sub Foo() '*** add custom extended implementation here MyBase.Foo() '*** call to base class implementation '*** add more custom extended implementation here End Sub End Class
'*** base class implementation
'*** add custom extended implementation here
MyBase.Foo() '*** call to base class implementation
'*** add more custom extended implementation here
Note the use of the Visual Basic .NET keyword MyBase. This keyword is used in a derived class to explicitly call public or protected members from its base class. In this example, the Class2 definition of Foo is making an explicit call to the Class1 definition of Foo. The MyBase keyword allows the derived class author to chain a call to the base class implementation. Note that you can chain the call to the base class implementation either before or after any code you'd like to add to the overriding method implementation. Let's review two important rules for chaining a call from a constructor in a derived class to a constructor in its base class. First, a constructor in a derived class can only call a constructor from its base class in the first executable line of its implementation. Second, a constructor in a base class can only be called once by a constructor defined in one of its derived classes. Chaining a call from derived class methods to base class methods does not have the same constraints as chaining a call to a constructor. The chained call doesn't have to be made at the beginning of the derived class implementation; it can be made in the middle or at the end of the overriding implementation. Furthermore, it is possible for an overriding implementation to call the base class implementation multiple times. Of course, the semantics of an overridable method might prohibit a derived class author from calling the base class implementation multiple times. However, you can create a design in which an overridable method allows an overriding implementation to chain multiple calls. Keep in mind that the MyBase keyword can be used to call any accessible member from the base class definition. This means that when you're writing the implementation for a method or property implementation in a derived class, you can directly access any visible field, method, or property defined in the base class. When you use the MyBase keyword to invoke a method or property, you will always bind to the base class implementation, whether it is overridden or not, because all calls made through the MyBase keyword use static binding, not dynamic binding.
You've just seen the syntax for creating overridable methods and properties, for overriding a method, and for chaining a call to an overridden base class implementation. Mastering the syntax is the easy part. Making sure you get the semantics right is much more difficult. Anyone who has managed a large software project using inheritance and method overriding can tell you that this requires expertise and plenty of attention to detail. It's easy to find your syntax errors because the compiler can catch them and tell you exactly where they are. But the compiler can't catch your semantic errors. Making sure the semantics for overridable methods are well-defined requires discipline and coordination across development teams. Let's take a closer look at managing the semantics of overridable methods. The base class author is responsible for defining a contract that contains the semantics for each overridable method. He must convey these requirements to all derived class authors in the form of some out-of-band mechanism such as documentation. You should also realize that when you create a class that inherits from a base class with overridable methods, you have additional responsibilities. Every derived class author is responsible for seeking out the documentation that describes the base class contract and understanding what constraints are placed on each method to be overridden. An overridable method complicates the programming contract of a base class because it gives a derived class author three different possible approaches. A derived class author can inherit a base class implementation and reuse it without modification, provide an overriding implementation that chains a call back to the base class implementation, or provide an overriding implementation that does not chain a call back to the base class implementation. Consider these three approaches—reusing, extending, and replacing—from a design perspective. When a derived class inherits a method, it is reusing the base class implementation. When it overrides a method and chains a call back to the base class, it is extending the base class implementation. When a derived class overrides a method and does not chain a call back the base class, it is replacing the base class implementation. While the CLR's support for inheritance allows for reusing, extending, or replacing the base class, many overridable methods have semantics that do not allow for all three approaches. The Finalize method of the class System.Object provides a good real-world example of this. If you elect to override the Finalize method in a user-defined class, your implementation is required to chain a call to the Finalize implementation from the base class. If you fail to do this, you have broken the semantic contract of this overridable method and there's a good chance your code will have problems. As you can see, some overridable methods such as Finalize only support the reuse or extension of the base class implementation. It's also common for an overridable method to have semantics that allow reusing and replacing, yet prohibit extending. In short, the semantics of overridable methods and properties can require some extra attention. The semantics involved with chaining can become even more complicated because some overridable methods have semantics that require an overriding implementation to chain a call to the base class implementation at a specific time. For example, the semantics of one overridable method could require overriding method implementations to chain a call to the base class implementation before doing any work in the derived class. On the other hand, the semantics of another overridable method could require overriding method implementations to chain a call to the base class implementation after all work has been completed in the derived class implementation. This discussion should lead to some important observations. First, the semantics of method and property overriding are often sensitive as to whether an overriding method should chain a call to its base class. Second, the semantics of overriding can be affected by whether the chained call should be made at the beginning or end of the overriding method or property implementation. When you design a base class, it is your responsibility to document the semantics for each overridable method and property. Your documentation should specify whether each overridable method and property requires you to chain a call to the base class implementation. You should also point out whether a chained call must be made at the beginning or at the end of the overriding implementation in the derived class. Even if you never design or write a base class definition, you must keep these rules in mind. Using the Microsoft® .NET Framework, you will almost certainly encounter situations in which you must create classes that inherit from one of the base classes of various .NET Framework libraries. Examples of such base classes are System.Windows.Forms.Form, System.Web.UI.Page, and System.Web.WebServices.WebService. For example, whenever you create a form-based application with Visual Basic .NET, you will implement one or more classes that inherit from System.Windows.Forms.Form. This class has several overridable methods and properties. Therefore, when you elect to override one method or property in a class that inherits from the Form class, you must inspect the documentation for the Form class to determine what constraints have been added to the semantics of each overridable member. Let me reiterate that the many problems related to overriding involve semantics and thus cannot be caught by the compiler. Only your attention to detail can ensure the correctness of your code. It's easy to get into trouble quickly by underestimating this.
A class created with Visual Basic .NET is inheritable by default. If you create a class named Class2 that inherits from Class1, another programmer can create a third class that inherits from your derived class (see Figure 1). Given these three class definitions, should the author of Class3 be able to override the Class2 implementation of Foo? The answer is yes. A method that is declared with the Overrides keyword is itself overridable. This means the author of Class3 can override your implementation in Class2 with the following code:
Class Class3 : Inherits Class2 Overrides Sub Foo() '*** method definition End Sub End Class
Class Class3 : Inherits Class2
You can take this example a step further by creating a class named Class4 that inherits from Class3. Class4 would be able to override the Class3 definition of the Foo method. Going even further, you could create Class5, Class6, and Class7. Each class could inherit from the one before it and override the Foo method with a new implementation. Theoretically, there isn't a limitation on how deep you can design an inheritance hierarchy, but there are often practical limitations. Let's look at a few examples of how to limit inheritance to keep a complicated design from getting out of control. Let's say you've created a definition for Class2 by inheriting from Class1. From your perspective, you are the beneficiary of inheritance because you are able to reuse code from Class1, and you've saved yourself a good deal of time in doing so. However, if you allow others to inherit from your derived class, you must also live up to all the responsibilities of a base class author. That includes documenting semantics for all your overridable methods. As you've just seen, when you override a method using the Overrides keyword, your method definition is overridable by default. You can reverse this default behavior by adding the NotOverridable keyword before the Overrides keyword, as shown in Figure 2. In this example, the author of Class3 is no longer allowed to override the Foo method. It's important for you to see that when you declare an overriding method implementation with the NotOverridable keyword, it simplifies your design. You don't have to worry about how other classes that inherit from your class might break the semantics of your method. You've just seen how using the NotOverridable keyword allows you to prohibit overriding on a method-by-method or a property-by-property basis. However, you have another option that can make things even easier. You can prevent any other programmer from inheriting from Class2 altogether. Last month I discussed defining a class with the NotInheritable keyword. Here's an example of how it can be applied to your derived class:
Class Class1 Overridable Sub Foo() '*** method definition End Sub End ClassNotInheritable Class Class2 : Inherits Class1 Overrides Sub Foo() '*** method definition End Sub End Class
NotInheritable Class Class2 : Inherits Class1
This really simplifies things because you no longer have to worry about breaking the contract between Class2 and its derived classes. As you have seen, sometimes it makes sense to define overridden methods and properties as NotOverridable. Other times it's better to define a derived class as NotInheritable. Most software developers agree that keeping a design as simple as possible is beneficial. However, there's another good reason to apply the NotOverridable and NotInheritable keywords whenever you can. It also can improve your performance. Remember that overridable methods require the use of dynamic binding, thus they incur a cost at runtime. A judicious use of the NotOverridable and NotInheritable keywords allows the Visual Basic .NET compiler to use static binding when it would otherwise have to use dynamic binding. For example, imagine that Class2 is defined with the NotInheritable keyword. The Visual Basic .NET compiler can make the assumption that a reference variable of type Class2 will only reference an object created from Class2. There will never be a case where a client uses a Class2 reference variable to access an object of some other Class2-compatible type. Since Class2 is sealed, a Class2 reference variable can only be used to access objects created from Class2. There is no opportunity for polymorphism and, consequently, there is no need to use dynamic binding. In such a case, the compiler will optimize calls by using static binding instead of dynamic binding. On the topic of static binding versus dynamic binding, it makes sense to discuss some subtle differences between the keywords Me, MyClass, and MyBase. All three can be used inside a method implementation of a class to call another method. However, each of the three can exhibit quite different behavior. Consider the class definitions in Figure 3. What's important here is the Class2 implementation of the Bar method. As you can see, there are four ways to call the Foo method. When you call MyBase.Foo, the Visual Basic .NET compiler uses static binding to invoke the implementation of Foo inside Class1. When you call MyClass.Foo, the compiler uses static binding to invoke the implementation of Foo in Class2. However, when you call Me.Foo, the compiler uses dynamic binding to invoke the most-derived implementation of Foo, which is defined in Class3. Finally, if you call Foo without one of these three keywords, it has the same effect as calling Me.Foo in that it uses dynamic binding. Note that calls through MyBase and MyClass always result in static binding. Calls through the Me keyword result in dynamic binding in any situation in which the method being called is declared as overridable. It's important to see that the usefulness of the keyword depends on the scenario.
As you know, when you create a class from a base class that implements a specific interface, the derived class also implicitly implements this interface. Suppose there's a class named Class1 that implements an interface named IFooable and that you are creating a derived class named Class2 that inherits from Class1 (see Figure 4). In this example, Class2 automatically supports IFooable, which is great if you're satisfied with the way Class1 implements IFooable.Foo. But what if you'd like to override IFooable.Foo with a Class2-specific implementation? That can be a tricky. A method definition (for instance, Class1.Foo) that implements a method from an interface is not overridable by default. Like a standard method, the author of Class1 must declare the Foo method as overridable (see Figure 5). Now the entry point in Class1 for IFooable.Foo has been declared with the Overridable keyword. This allows a derived class such as Class2 to override the Class1 implementation. In less common scenarios, the author of Class1 might want to implement IFooable, but not expose the Foo method as public in the Class1 definition. The author of Class1 can accomplish this by declaring the method implementation as either private or protected. However, the author of Class1 must declare the entry point for IFooable.Foo as protected rather than private if the implementation will be overridable. Examine the code in Figure 6. Neither Class1 nor Class2 expose a public Foo method as part of their class contract. However, clients that use an IFooable reference variable to access an object created from either Class1 or Class2 can call the Foo method. Also note that clients that call the Foo method through an IFooable reference variable on an object created from Class2 will invoke the overriding implementation in Class2.
As you've seen so far, there are two important programming contracts that a class author has to worry about: the contract that a class promises its clients and the contract that a class has with its derived classes. You have already seen a technique for declaring a class with the NotInheritable keyword. This prevents inheritance, eliminating the need for a class author to worry about a contract with derived classes. Now I'll show you a technique that allows you to create a class that is not a creatable type. Classes that allow clients to instantiate objects using the New operator are commonly referred to as concrete classes. However, there are times when it makes sense to design using classes that do not support instantiation via the New operator. Such classes are not creatable and are often referred to as abstract classes. It's a common approach for a designer to use a base class that is defined as an abstract class. In abstract classes, the base class is defined as abstract and all the derived classes that inherit from the base class are defined as concrete classes. This allows a base class author to do something that isn't possible in a concrete base class: add public methods and properties that don't have an implementation. When a class author adds an implementation-free method or property to a base class, it's known as an abstract member. Note that an abstract member can only exist in an abstract class. The primary motivation for programmers to design with abstract methods or properties is that the base class author gets to define the calling syntax and semantics. However, the responsibility for writing the implementations for abstract members is delegated to derived class authors. Let's define a simple abstract class with Visual Basic .NET to put things into perspective. You can define an abstract class in Visual Basic .NET using the MustInherit keyword, which prevents clients from instantiating objects with the New operator.
MustInherit Class Class1 '*** class definitionEnd ClassModule MyApp Sub Main() Dim obj As New Class1 '*** compile-time error Ens SubEnd Module
MustInherit Class Class1
'*** class definition
Dim obj As New Class1 '*** compile-time error
The designers of Visual Basic .NET selected the MustInherit keyword because it conveys the idea that an abstract class is only useful in designs that involve inheritance. In other words, if your design involves an abstract base class, it should also involve concrete classes that inherit from it. A base class author can define a client-side programming contract and a partial implementation for this contract. The author of any concrete class that inherits from the base class must complete any missing implementation details. How is it that an abstract base class can provide a client-side programming contract with a partial implementation? It's possible because the base class author can define some methods and properties with an implementation and define other methods and properties without an implementation. When you create an abstract class, you get to decide which members provide an implementation and which are abstract. When you want to add an abstract member to an abstract class, you must declare it using the MustOverride keyword. Remember that abstract members can only exist inside abstract classes. Therefore, it is illegal to declare a method or property using the MustOverride keyword inside a class that is not defined with the MustInherits keyword. Defining a member using the MustOverride keyword is just like defining a member in an interface. The abstract class definition determines the syntax for accessing the member as well as the member's semantics. Take a look at Figure 7, in which the Person class is defined as an abstract class. This class is only useful in designs where it is employed as a base class. The Person class defines an abstract method named Speak. Any concrete class that inherits from the Person class must implement Speak. The author of the Person class has added this method to the client-side programming contract. However the responsibility of implementing this method has been delegated to derived class authors. As shown in Figure 7, the concrete class named Programmer inherits from the abstract class named Person. The Programmer class provides its own implementation for the abstract method named Speak. However, the Administrator class does not contain an implementation of Speak, so it will not compile. Any concrete class that inherits from an abstract class must implement every abstract method and property. Think about how polymorphism is achieved in this last example. The Person class defined a client-side programming that included the Speak method. Now it's possible to write generic client-side code that can use an object created from any Person-derived class.
'*** accepts any Person-compatible objectSub ProcessPerson(obj As Person) obj.SpeakEnd Sub
'*** accepts any Person-compatible object
Sub ProcessPerson(obj As Person)
Even though there will never be an object created from the Person class, there will be objects created from concrete classes that inherit from the Person class. Each one of these concrete classes is guaranteed to supply its own custom implementation of the Speak method. This results in objects with differing behaviors that can be easily substituted for one another and demonstrates how abstract methods can be used to achieve polymorphism.
No doubt about it, inheritance is a very complicated and tricky subject. There isn't a person in the world who really understands inheritance who will tell you that it's easy to learn. Throughout my discussion on inheritance, you've seen many concepts that are challenging for programmers to absorb. You've also seen several examples that show how easy it is to get into trouble when you use inheritance incorrectly. The first question you should address is whether it makes sense for you to use inheritance in your own designs. Should you be designing and writing base classes that will be inherited by other derived classes? Once you've made a decision to use inheritance, you take on all the responsibilities of a base class author that I've discussed. Keep in mind that it's quite possible to live a long and fulfilling life as a programmer using .NET applications and never have the need to create your own base class. Even if you never create your own base class, you will probably create custom classes that inherit from someone else's base class. It's hard to imagine that you could program using the .NET class libraries very long before you'll be required to create a custom class that inherits from a system-provided base class such as the Form class, the Page class or the WebService class. Because so many of the class libraries in the .NET Framework have designs that involve base classes, every developer using .NET needs to understand the responsibilities of a derived class author. If you don't have a solid understanding of the issues, it will be difficult for you to write the code for a derived class correctly. When I think of what's recently happened to Visual Basic, it reminds me of that ancient proverb: be careful what you ask for; you just may receive it. Most programmers using Visual Basic have been asking for inheritance, the holy grail of object-oriented programming, and now that wish has been granted. As a result, you have new powers and capabilities that you've never had before. However, one thing is undeniable: the bar has definitely been raised on what it takes to be a competent programmer using Visual Basic.Send questions and comments for Ted to firstname.lastname@example.org.
More MSDN Magazine Blog entries >
Browse All MSDN Magazines
Subscribe to MSDN Flash newsletter
Receive the MSDN Flash e-mail newsletter every other week, with news and information personalized to your interests and areas of focus.