Dynamic Overload Resolution
Join the DZone community and get the full member experience.
Join For FreeBoth when coding in C++ and C# I’ve had problems with overload resolution being static. There are workarounds, for example the visitor pattern but it requires quite an effort to implement. More importantly it cannot be implemented without changing the visited element. With C#4′s dynamic keyword there is finally a better solution.
To illustrate the problem I’ll implement the ITransportationService interface.
public interface ITransportationService { void TransportAnimal(Animal a); }
I’ll implement it by delegating the transportation work to the AnimalTransport class. The problem is that AnimalTransport has a special overload for elephants and the ITransportationService.TransportAnimal interface doesn’t.
A Simple Transport
First I’ll show a simple transport, which makes use of the AnimalTransport class.
public static class AnimalTransport { public static void LoadAnimal(Animal animal) { Debug.WriteLine("Putting " + animal.Name + " in a cage."); } public static void LoadAnimal(Elephant elephant) { Debug.WriteLine("Loading " + elephant.Name + " on a trailer."); } } public class SimpleTransportation : ITransportationService { public void TransportAnimal(Animal a) { AnimalTransport.LoadAnimal(a); } }
Executing the code shows a problem.
Transporting an elephant with SimpleTransportation... Putting Hanibal in a cage.
I think that it might be a problem trying to put Hanibal into an ordinary size pet cage.
A Flexible Transport with Dynamic
Introducing the dynamic keyword handles the situation.
public class FlexibleTransportation : ITransportationService { public void TransportAnimal(Animal a) { AnimalTransport.LoadAnimal((dynamic)a); } }
Executing the code now loads Hanibal on a trailer instead.
Transporting an elephant with FlexibleTransportation... Loading Hanibal on a trailer.
Objectoriented Principles
Objectorientation is all about separation of concerns and encapsulation of behaviour. In most cases, it is appropriate to defer to the class itself to decide behaviour that depends on the runtime type. For example, a RunDistance method on the Animal class would be an ordinary virtual (and probably abstract) method, letting each concrete implementation return how fast the specific animal runs.
In this case however I don’t think that it is appropriate. The animal
itself should be completely ignorant about how it is transported.
Looking at the AnimalTransport class I think that it is
completely reasonable to have a special overload for the elephant. It is
huge and requires special handling compared to ordinary animals like
cats and dogs.
It is also totally appropriate that the ITransportation interface knows very little about the details of the Animal
class hierarchy. Adding a separate overload (if we are at all allowed
to change it) there for the elephant would reveal details at the wrong
place.
As I mentioned in the beginning of the post the visitor pattern can solve the problem. Unfortunately that requires that the Animal class hierarchy has support for visitors, which is probably not the case if it comes from a library.
I think that using dynamic in this case makes the code clean, elegant and still type safe. If all other classes are already existing and not possible to change, the only other alternative would be manually check the type during runtime.
Opinions expressed by DZone contributors are their own.
Comments