Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I have a base class. How do I call the right method for the right derived class?

Obviously trying to simplify the problem here. I have a base class and a number of derived classes:

public class Mammal { }

public class Cat : Mammal { } 

public class Dog : Mammal { }

And a utility class:

public static class AnotherClass
{
    public static void GiveFood(Cat cat) {}
    public static void GiveFood(Dog dog) {}
}

Somewhere else is a method, Feed, which takes a Mammal, and from within there i want to call the right overload on AnotherClass:

public void Feed(Mammal mammal) {
    // if mammal is a cat, call the AnotherClass.GiveFood overload for cat,
    // if it's a dog, call the AnotherClass.GiveFood for dog, etc.
}

One way to do that would be to do something like:

public void Feed(Mammal mammal) {
    if (mammal is dog) 
        AnotherClass.GiveFood((Dog)mammal);
    if (mammal is Cat) 
        AnotherClass.GiveFood((Cat)mammal);
}

...but I actually have a huge number of animals derived from Mammal. Is there a nicer way to do what I want to do in Feed()? Is there any way I can avoid having Feed() end up being a huge ugly method filled with these "if x is y then call z"-statements?

like image 955
Zissou Avatar asked Dec 01 '22 19:12

Zissou


2 Answers

I don't usually like using dynamic, but this is one of the cases where I think it's appropriate:

public void Feed(Mammal mammal) {
  Anotherclass.GiveFood((dynamic)mammal);
}

That will resolve the correct overload at runtime, without knowing the type in advance.

Strictly speaking, this probably isn't going to be the fastest method, but as you point out, the alternatives can be a real pain to maintain, and/or hard to read. In this case, dynamic dispatch is elegant and will automatically incorporate any overloads you add in the future.

As Chris Sinclair points out, you could also add a catchall method to detect any invalid calls and provide a friendlier exception than the runtime error you'd receive if no matching GiveFood() overload could be found:

public static class AnotherClass
{
  public static void GiveFood(Cat cat) {}
  public static void GiveFood(Dog dog) {}

  public static void GiveFood(Mammal mammal)
  {
    throw new AnimalNotRecognizedException("I don't know how to feed a " + mammal.GetType().Name + ".");
  }
}
like image 76
4 revs Avatar answered Dec 04 '22 09:12

4 revs


I think it's the animal's responsibility to process food, not the feeder. Otherwise you'll run into the problem you now have:

public void Feed(Mammal mammal) {
    if (mammal is Duck) 
    {
        ((Duck)mammal).PryOpenBeak();
        ((Duck)mammal).InsertFeedingTube();
        ((Duck)mammal).PourDownFood();
    }
}

And so on, although ducks aren't mammals.

Anyway, your Mammal class should have an abstract method Feed(Food food), and the animal itself will have to figure out how to process the food. This way when later adding a new mammal, you won't have to update the feeder with the feeding logic for this new mammal.

@Chris's comment: then the animal could implement the proper IFoodXEater interface that contains a Feed(IFoodX) method, and then the feeder can look that up, although then you're back at square one:

if (mammal is IFishEater)
{
    ((IFishEater)mammal).Feed(new Fish());
}
like image 29
CodeCaster Avatar answered Dec 04 '22 08:12

CodeCaster