Replace Conditional with Polymorphism revisited

03/03/11

Replace Conditional with Polymorphism revisited

Switch statements and casts are code smells. A classic solution is the Replace Conditional with Polymorphism refactoring. But what if the class hierarchy is not under your control?

The case I encountered was overriding TrackingParticipant.Track in Workflow Foundation 4. Implementations get a TrackingRecord, but will generally want to process it based on its runtime type. Even the Microsoft sample uses a bunch of if statements with as casts.

One solution I’ve found is to use the dynamic keyword introduced in C# 4.0 combined with overload resolution. Here’s an example for LINQPad (add a namespace import of Microsoft.CSharp.RuntimeBinder):

void Main()
{
    MakeAnimalNoise(new Dog());
    MakeAnimalNoise(new Zebra());
    MakeAnimalNoise(new Cat());
}

public void MakeAnimalNoise(Animal animal)
{
    try
    {
        MakeNoise((dynamic) animal);
    }
    catch (RuntimeBinderException ex)
    {
        Console.WriteLine(
            "Unsupported animal: " + animal.GetType());
    }
}

public void MakeNoise(Dog dog)
{
    Console.WriteLine("Woof!");
}

public void MakeNoise(Cat cat)
{
    Console.WriteLine("Meow!");
}

public abstract class Animal {}
public class Dog : Animal {}
public class Cat : Animal {}
public class Zebra : Animal {}

Results:

Woof!
Unsupported animal: UserQuery+Zebra
Meow!

(The “UserQuery+” part is due to LINQPad.)

While there may be performance implications, the code becomes very maintainable.

Update - March 5, 2011

It’s really fast!


Your Host: webmaster@truewill.net
Copyright © 2000-2013 by William Sorensen. All rights reserved.