Chapter
4: Inheritance
Chapter 3, “Objects and Types,”
examined how to use individual classes in C#. The focus in that
chapter was on how to define methods, constructors, properties, and
other members of a single class (or a single struct). Although you
did learn that all classes are ultimately derived from the class
System.Object, you did not see how to
create a hierarchy of inherited classes. Inheritance is the subject
of this chapter. It briefly discusses the scope of C#’s support for
inheritance before examining in detail how to code first
implementation inheritance and then interface inheritance in C#.
Note that this chapter presumes familiarity with the basic concepts
of inheritance, including virtual functions and overriding. This
chapter concentrates on the syntax used to provide inheritance and
inheritance-related topics, such as virtual functions, and on those
aspects of the C# inheritance model that are particular to C# and
not necessarily shared by other object-oriented languages.
Types of Inheritance
This chapter starts off by reviewing exactly
what C# does and does not support as far as inheritance is
concerned.
Implementation versus Interface Inheritance
In object-oriented programming, there are two
distinct types of inheritance: implementation inheritance and
interface inheritance.
-
Implementation
inheritance means that a type derives from a base type, taking
all the base type’s member fields and functions. With
implementation inheritance, a derived type adopts the base type’s
implementation of each function, unless it is indicated in the
definition of the derived type that a function implementation is to
be overridden. This type of inheritance is most useful when you
need to add functionality to an existing type, or where a number of
related types share a significant amount of common functionality. A
good example of this comes in
the Windows Forms classes, which are discussed in Chapter
28, “Windows Forms,” along with the base class
System.Windows.Forms.Control, which
provides a very sophisticated implementation of a generic Windows
control, and numerous other classes such as System.Windows.Forms.TextBox and System.Windows.Forms.ListBox that are derived from
Control and override functions or
provide new functions to implement specific types of control.
-
Interface inheritance
means that a type inherits only the signatures of the functions but
does not inherit any implementations. This type of inheritance is
most useful when you want to specify that a type makes certain
features available. For example, certain types can indicate that
they provide a resource cleanup method called Dispose() by deriving from an interface,
System.IDisposable (see Chapter
11, “Memory Management and Pointers”). Because the way
that one type cleans up resources is likely to be very different
from the way that another type cleans up resources, there is no
point in defining any common implementation, so interface
inheritance is appropriate here. Interface inheritance is often
regarded as providing a contract: By deriving from an interface, a
type is guaranteed to provide certain functionality to clients.
Traditionally, languages such as C++ have been very
strong on implementation inheritance. Indeed, implementation
inheritance has been at the core of the C++ programming model. On
the other hand, Visual Basic 6 did not support any implementation
inheritance of classes but did support interface inheritance thanks
to its underlying COM foundations.
C# has both implementation and interface
inheritance. There is arguably no preference, because both types of
inheritance are fully built into the language from the ground up.
This makes it easy for you to choose the best architecture for your
solution.
Multiple Inheritance
Some languages such as C++ support what is
known as multiple inheritance, in which a
class derives from more than one other class. The benefits of using
of multiple inheritance are debatable: On the one hand, there is no
doubt that it is possible to use multiple inheritance to write
extremely sophisticated, yet compact, code, as demonstrated by the
C++ ATL library. On the other hand, code that uses multiple
implementation inheritance is often difficult to understand and
debug (a point that is equally well demonstrated by the C++ ATL
library). As mentioned, making it easy to write robust code was one
of the crucial design goals behind the development of C#.
Accordingly, C# does not support multiple implementation
inheritance. It does, however, allow types to be derived from
multiple interfaces. This means that a C# class can be derived from
one other class, and any number of interfaces. Indeed, we can be
more precise: Thanks to the presence of System.Object as a common base type, every C# class
(except for Object) has exactly one base
class, and may additionally have any number of base interfaces.
Structs and Classes
Chapter 3 distinguishes between structs
(value types) and classes (reference types). One restriction of
using a struct is that structs do not support inheritance, beyond
the fact that every struct is automatically derived from
System.ValueType. In fact, we should be
more careful. It’s true that it is not possible to code a type
hierarchy of structs; however, it is possible for structs to
implement interfaces. In other words, structs don’t really support implementation
inheritance, but they do support interface inheritance. Indeed, we
can summarize the situation for any types that you define as
follows:
-
Structs are always
derived from System.ValueType. They can
also be derived from any number of interfaces.
-
Classes are always
derived from one other class of your choosing. They can also be
derived from any number of interfaces.
|