Search code examples
c#polymorphismdowncast

Polymorphic Downcasting in c#?


I'm trying to make food for my IAnimals using a FoodFactory and have a large List<IAnimal> of Dogs and Foxs etc. Ideally, I would pass in an IAnimal and the FoodFactory would return the right food but I am not sure how to do this.

interface IAnimal {
   void MakeNoise();
}
public sealed class Dog : IAnimal {
    public void MakeNoise() { Console.WriteLine("Woof woof"); }
}
public sealed class Fox : IAnimal {
    public void MakeNoise() { Console.WriteLine("What does the fox say?"); }
}
public static class FoodFactory {
    public static void Create(Dog dog) {
        Console.WriteLine("return dog food");
    }
    public static void Create(Fox fox) {
        Console.WriteLine("return fox food");
    }
}

and here's my caller code:

var myList = new List<IAnimal> { new Dog(), new Fox() };
foreach (var animal in myList)
{
    FoodFactory.Create(animal); // Sadly, this doesn't work
}

Is there a 'proper' way to do this? All I can think of is a load of if statements like:

if (animal is Dog) { FoodFactory.Create(animal as Dog); }

I have a lot of animals and I'd much rather the computer work out which one it's dealing with than me! Thank you.

UPDATE

Thank you to all that have posted. I went with Xinchao's answer because it was most applicable in my context; it didn't affect my animal classes and allowed the type to be resolved at runtime without me checking all the animal types. There were three helpful answers which would have worked (e.g. using visitor pattern) but they weren't quite applicable in my context as I don't want to change/extend my animals.


Solution

  • The reason you are having this problem is the so-called overload resolution happens at compile time instead of run-time. There are a couple of ways to solve it: Using a bunch of if-else-if statements is certainly one way, although you can write it prettier.

    With .Net 4.0 or above, you can use to "dynamic" keyword to delay the overload resolution to run-time. It has a performance penalty, but it achieves accurately what you need:

    public static class FoodFactory {
         public static void Create(dynamic animal) {
            InternalCreate (animal);
        }
        private static void InternalCreate (Dog dog) {
            Console.WriteLine("return dog food");
        }
        private static void InternalCreate (Fox fox) {
            Console.WriteLine("return fox food");
        }
    }
    

    To add a bit more type safety, you might consider to do something like:

    public static class FoodFactory {
        public static void Create(IAnimal animal) {
            Dispatch (animal);
        }
    
        private static void Dispatch (dynamic animal) {
            InternalCreate (animal);
        }
        private static void InternalCreate (Dog dog) {
            Console.WriteLine("return dog food");
        }
        private static void InternalCreate (Fox fox) {
            Console.WriteLine("return fox food");
        }
    }