You need to build several classes that share many common traits. These classes may share common properties, methods, events, delegates, and even indexers; however, the implementation of these may be different for each class. These classes should not only share common code but also be polymorphic in nature. That is to say, code that uses an object of the base class should be able to use an object of any of these classes in the same manner.
Use an abstract base class to create polymorphic code. To demonstrate
the creation and use of an abstract base class, here is an example of
three classes, each defining a media type: magnetic, optical, and
punch card. An abstract base class, Media
, is
created to define what each derived class will
contain:
public abstract class Media { public abstract void Init( ); public abstract void WriteTo(string data); public abstract string ReadFrom( ); public abstract void Close( ); private IntPtr mediaHandle = IntPtr.Zero; public IntPtr Handle { get {return(mediaHandle);} } }
Next, the three specialized media type classes, which inherit from
Media
, are defined to override each of the
abstract members:
public class Magnetic : Media { public override void Init( ) { Console.WriteLine("Magnetic Init"); } public override void WriteTo(string data) { Console.WriteLine("Magnetic Write"); } public override string ReadFrom( ) { Console.WriteLine("Magnetic Read"); string data = ""; return (data); } public override void Close( ) { Console.WriteLine("Magnetic Close"); } } public class Optical : Media { public override void Init( ) { Console.WriteLine("Optical Init"); } public override void WriteTo(string data) { Console.WriteLine("Optical Write"); } public override string ReadFrom( ) { Console.WriteLine("Optical Read"); string data = ""; return (data); } public override void Close( ) { Console.WriteLine("Optical Close"); } } public class PunchCard : Media { public override void Init( ) { Console.WriteLine("PunchCard Init"); } public override void WriteTo(string data) { Console.WriteLine("PunchCard WriteTo"); } public override string ReadFrom( ) { Console.WriteLine("PunchCard ReadFrom"); string data = ""; return (data); } public override void Close( ) { Console.WriteLine("PunchCard Close"); } }
The following methods, TestMediaABC
and
UseMedia
, show how any of the three media types
can be used polymorphically from within the
UseMedia
method:
public void TestMediaABC( ) { Media x = new Magnetic( ); UseMedia(x); Console.WriteLine( ); x = new Optical( ); UseMedia(x); } private void UseMedia(Media media) { media.Init( ); media.WriteTo("text"); media.ReadFrom( ); Console.WriteLine(media.Handle); media.Close( ); Console.WriteLine(media.ToString( )); }
The output of these methods is shown here:
Magnetic Init Magnetic Write Magnetic Read 0 Magnetic Close Magnetic Optical Init Optical Write Optical Read 0 Optical Close Optical
Polymorphism through an abstract base class is a powerful tool. With
this tool, you are able to create a method
(UseMedia
in this solution) that accepts a
parameter whose specific type is known only at runtime. Since the use
of this parameter is similar for all objects that can be passed in to
this method, we do not have to worry about the specific class that is
passed in; we need to know only how the abstract base class is
defined. It is through this abstract base class definition that we
know how to use the specific type.
There are several things to keep in mind when using an abstract base class:
Neither this class nor its abstract members can be declared as
sealed
; this would defeat polymorphism.
The abstract class cannot be instantiated using the
new
operator, but a variable can be declared as an
abstract base class type.
All abstract members must be overridden in a derived class unless the derived class is also abstract.
It is implied that an abstract method is also defined as
virtual
.
Only methods, properties, indexers, and events may be declared as abstract.
Abstract methods, properties, and indexers may not be declared as
static
or virtual
.
If an abstract base class implements an interface, it must provide either an implementation for the interface members or an abstract definition of the interface members. A combination of the two may be applied as well.
An abstract base class can contain abstract and nonabstract members. It is not required to contain any abstract members, but this omission may confuse those who read this code.
An abstract class may implement any number of interfaces and may also
inherit from a single class. As a note, abstract members may override
virtual
members in the nonabstract base class.
A derived class can override abstract
properties
and must include at least one accessor method (i.e.,
get
or set
.) A property in a
base class that overrides an abstract
property
implementing only a get
or a
set
accessor must override that specific
get
or set
accessor. If the
abstract
property implements both a
get
and a set
accessor, the
overriding base class property may override one or both accessors.
A structure cannot implement polymorphism through an abstract base class/structure. Instead, a structure should consider implementing polymorphism through interfaces (see Recipe 3.17).
It is possible to use interfaces to implement polymorphism; this is discussed at length in Recipe 3.17. There are several advantages to using an abstract base class over an interface:
Abstract base classes allow more flexibility in versioning. An abstract base class can add a nonabstract member without breaking existing derived classes; an interface cannot. However, adding a new abstract member will break the existing derived classes.
An abstract base class can contain abstract members as well as nonabstract members. An interface may contain only definitions of members with no implementation.
You should also consider using an abstract base class over an
interface when a lot of disparate members need to be overridden in
the derived classes. For example, if you are implementing a set of
members that control searching or sorting of items, you should
initially consider interfaces, since this is a focused set of members
that may be implemented over a wide range of unrelated classes. If
you were implementing a set of members that determines the base
functionality for a complete type, such as the
Media
type, you would want to use an abstract base
class. See Recipe 3.17 for the advantages of
using interface polymorphism over abstract base classes.
Notice that the abstract
Media
class in this recipe could be written as a
concrete class (i.e., remove the abstract
keyword
and add implementations to all abstract methods). This would allow
you to create objects from the Media
class. If you
do not wish for objects to be created from your base class
(Media
), you should make it abstract.
See Recipe 3.17; see section 10.1.1.1, “Abstract Classes”, in the C# Language Specification.