Recipe3.16.Implementing Polymorphism with Interfaces


Recipe 3.16. Implementing Polymorphism with Interfaces

Problem

You need to implement polymorphic functionality on a set of existing classes. These classes already inherit from a base class (other than Object), thus preventing the addition of polymorphic functionality through an abstract or concrete base class.

In a second situation, you need to add polymorphic functionality to a structure. Abstract or concrete classes cannot be used to add polymorphic functionality to a structure.

Solution

In these circumstances, as opposed to those explored in Recipe 3.4, implement polymorphism using an interface instead of an abstract or concrete base class. The code shown here defines two different classes that inherit from List<T>:

 public class InventoryItems<T> : List<T> {     // … } public class Personnel<T> : List<T> {     // … } 

You want to add the ability to print from either of these two objects polymorphically. To do this, create an interface called IPrint that defines a Print method to be implemented in a class:

 public interface IPrint {     void Print( ); } 

Implementing the IPrint interface on the InventoryItems<T> and Personnel<T> classes gives you the following code:

 public class InventoryItems<T> : List<T>, IPrint {     public void Print()     {         foreach (T obj in this)         {             Console.WriteLine("Inventory Item: " + obj);         }     } } public class Personnel<T> : List<T>, IPrint {     public void Print()     {         foreach (T obj in this)         {             Console.WriteLine("Person: " + obj);         }     } } 

The following two methods, TestIPrintInterface and CommonPrintMethod, show how any object that implements the IPrint interface can be passed to the CommonPrintMethod polymorphically and printed:

 public void TestIPrintInterface() {     // Create an InventoryItems object and populate it.     IPrint obj = new InventoryItems<string>();     ((InventoryItems<string>)obj).Add("Item1");     ((InventoryItems<string>)obj).Add("Item2");     // Print this object.     CommonPrintMethod(obj);     Console.WriteLine();     // Create a Personnel object and populate it.     obj = new Personnel<string>();     ((Personnel<string>)obj).Add("Person1");     ((Personnel<string>)obj).Add("Person2");     // Print this object.     CommonPrintMethod(obj); } private void CommonPrintMethod(IPrint obj) {     Console.WriteLine(obj.ToString());     obj.Print(); } 

The output of these methods is shown here:

 CSharpRecipes.ClassAndStructs+InventoryItems`1[System.String] Inventory Item: Item1 Inventory Item: Item2 CSharpRecipes.ClassAndStructs+Personnel`1[System.String] Person: Person1 Person: Person2 

Discussion

The use of interfaces is found throughout the FCL. One example is the IComparer interface: this interface requires a class to implement the Compare method, which compares two objects to determine if one is greater than, less than, or equal to another object. This method is used by the Array, ArrayList, and List<T> Sort and BinarySearch static methods to allow sorting and searching to be performed on the elements contained in an array. For example, if an array contains objects that implement a custom IComparer interface, the static Sort and BinarySearch methods will use this interface to customize their sorting/searching of elements in that array.

Another example is found in the IEnumerable and IEnumerator interfaces. These interfaces let you iterate over items in a container using the foreach loop. It does not matter what the contained items are or what the containing object is. The foreach loop can simply use these interfaces regardless of the type of objects that implement them.

In many cases, you will choose to implement polymorphism through abstract base classes, as discussed in Recipe 3.4; however, there are some cases in which interfaces are superior. Interfaces should be considered before abstract base classes in the following cases:

  • When several unrelated classes need to implement a common subset of their functionality polymorphically. The Solution to this recipe demonstrates this concept.

  • If one or more of the classes already inherits from a base class, an interface may be added to implement polymorphism. In the Solution for this recipe, for instance, the InventoryItem class could have inherited from an existing Item class. This would make it impossible to use an abstract base class. An interface can be added in this case to implement polymorphism.

  • If, in future versions of your data type, you will want to add new polymorphic functionality without breaking the existing interface of your data type. Interface polymorphism provides better versioning than abstract or concrete base classes. To add new polymorphic functionality, implement a new interface containing this functionality on your existing data type.

  • When you need to implement polymorphism on value types.

Implementing polymorphism through interfaces works not only on reference types, but also with value types. Value types cannot derive from any other type except ValueType; this prevents them from overriding an abstract base class. You must instead use interfaces to implement polymorphism. This can be shown by the following structure declarations:

 public struct InventoryItems<T> : List<T>, IPrint public struct Personnel<T> : List<T>, IPrint 

These structures can act polymorphically on the IPrint interface.

These structures now can act polymorphically on the IPrint interface. When implementing an interface on a structure, be aware that a boxing operation will be performed whenever the value is cast to the interface type (in this case, the IPrint interface). The boxed object is a copy of the original structure. This means that if you modify the boxed object, using a reference to the interface, you will be modifying a copy of the original structure.

See Also

See Recipe 3.4; see the "interface Keyword" topic in the MSDN documentation.



C# Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2004
Pages: 424

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net