Recipe 3.18 Calling the Same Method on Multiple Object Types
Problem
You need to
perform a particular action on a set of dissimilar objects contained
within an array or collection, preferably without having to know each
individual object's type.
Solution
Use interfaces in a polymorphic
manner. The following interface contains a single method,
Sort, which allows sorting to be performed on the
object that implements this interface:
public interface IMySort
{
void Sort( );
}
The next three classes implement the IMySort
interface. These classes all share the same Sort
method, but each class implements it in a different way:
public class CharContainer : IMySort
{
public void Sort( )
{
// Do character type sorting here
Console.WriteLine("Characters sorted");
}
}
public class NumberContainer : IMySort
{
public void Sort( )
{
// Do numeric type sorting here
Console.WriteLine("Numbers sorted");
}
}
public class ObjectContainer : IMySort
{
public void Sort( )
{
// Do object type sorting here
Console.WriteLine("Objects sorted");
}
}
The SortAllObjects method accepts an array of
objects :
public void SortAllObjects(IMySort[] sortableObjects)
{
foreach (IMySort m in sortableObjects)
{
m.Sort( );
}
}
If this method is called as follows:
Obj.SortAllObjects(new IMySort[3] {new CharContainer( ),
new NumberContainer( ),
new ObjectContainer( )});
the following is displayed:
Characters sorted
Numbers sorted
Objects sorted
Discussion
The foreach
loop is useful not only for iterating over individual elements in a
collection or an array, but also in iterating over a specific
interface implemented by each element in a collection or array. Using
this technique, interface members may be used in a similar manner on
each element, even if the elements are unrelated object types.
Consider the following array of objects:
Object[] objs = new Object[6] {new CharContainer( ),
new NumberContainer( ),
new CharContainer( ),
new ObjectContainer( ),
new NumberContainer( ),
new ObjectContainer( )});
This array contains several objects of differing types. The one
thread of similarity that runs through each type is the
implementation of the IMySort interface, defined
as follows:
public interface IMySort
{
void Sort( );
}
Passing the Objects array in to the following
method allows each Sort method to be called from
each object in the Objects array:
public void SortAllObjects(object[] sortableObjects)
{
foreach (IMySort m in sortableObjects)
{
m.Sort( );
}
}
The foreach loop in this method is able to treat
each object in the sortableObjects array in the
same way because each object in the
sortableObjects array is cast to its
IMySort interface and used as such.
If the foreach loop encounters a
sortableObjects array that contains one or more
objects that do not implement the IMySort
interface, an
InvalidCastException will be thrown. To prevent an
exception from being thrown while at the same time allowing the
foreach loop to iterate over all elements in the
sortableObjects array, you can use the following
modified code:
public void SortAllObjects(object[] sortableObjects)
{
foreach (object o in sortableObjects)
{
IMySort sortObject = o as IMySort;
if (sortObject!= null)
{
sortObject.Sort( );
}
}
}
This modified method will now test each element of the
sortableObjects array to first determine whether
it can be cast to an IMySort interface. If it can
be cast to this interface type, the variable
sortObject will not be null and
the if statement will allow the
Sort method on that object to be called.
See Also
See the "interface" keyword,
"Base Class Usage Guidelines," and
"When to Use Interfaces" topics in
the MSDN documentation.
|