This chapter introduced you to abstract functions, polymorphism and interfaces. A review of the important points in this chapter follows.
An abstract method consists of a method header but has no method body and, therefore, no implementation. Properties and indexers can also be declared abstract.
A class with one or more abstract functions must itself be declared abstract. An abstract class can contain non-abstract function members.
An abstract class cannot be instantiated.
A class derived from an abstract class must provide implementations for all the abstract methods contained in this base class; otherwise, it also becomes abstract. abstract functions are implicitly virtual, so providing an implementation for an inherited abstract class is done in the same way as overriding a virtual method.
Polymorphism generally means the ability to have many forms. In computer science, it means the ability of a variable to reference different object types and call different method implementations with the same method call. The underlying mechanism that makes this scenario possible is called dynamic binding.
Polymorphism is a powerful concept because it allows programmers to program against a general interface (set of method calls) while the specific implementations called are determined by the runtime.
A polymorphic variable, through which polymorhic calls are made to methods of different classes, can reference objects of different descendant classes. When an object of a descendant class is assigned to such a variable of an ancestor class, the object loses part of its identity. This is called up casting. Only the class members defined for the ancestor class can be called, even though the object the variable is referencing contains additional class members. It is possible to determine the identity of the hidden object with the is operator. A down cast using the cast operator or the as operator can be applied to regain the lost type information.
All classes are ultimately derived from the System.Object class of the .NET Framework class library. Consequently, all classes inherit its six non-static methods.
When you write a function in a derived class with the same signature and return type as a non-virtual function in the base class, you are hiding the method in the base class. Dynamic binding is not applied to hidden functions. Even if the function in the base class is virtual, you can still hide it by applying the new keyword to the function in the derived class.
Upgrading the class libraries on which your program depends can lead to a number of problems and several possible solutions. Collectively, these issues are referred to as versioning. By using the keywords virtual, new, and override wisely, you can prevent several problems often encountered in connection with inheritance when class libraries are upgraded.
Even though most of your functions are likely to be virtual, a function defaults (without the virtual keyword) to be a non-virtual function. This is because virtual functions can create more problems in descendant classes than non-virtual methods, and because they are slightly slower than non-virtual methods.
In multiple inheritance, a class can have more than one base class. C# does not support multiple inheritance. Instead, it applies interfaces.
An interface only contains abstract function members and events. This avoids the conflicting instance variable names and implementations found with multiple inheritance. Consequently, whereas a class only can have one base class, it can implement many interfaces.
Use interfaces when you need several classes to have common function headers that are not present in a common ancestor class. This will allow you to apply polymorphism to a group of classes independently of where these classes are located in their class hierarchies.
When several implementations are identical apart from the types the implementation is applied on, we can write a generic implementation instead. Generic implementations are made possible through polymorphism.