我有一个基类.如何为正确的派生类调用正确的方法?

Zis*_*sou 4 c# oop inheritance

显然试图在这里简化问题.我有一个基类和一些派生类:

public class Mammal { }

public class Cat : Mammal { } 

public class Dog : Mammal { }
Run Code Online (Sandbox Code Playgroud)

和实用类:

public static class AnotherClass
{
    public static void GiveFood(Cat cat) {}
    public static void GiveFood(Dog dog) {}
}
Run Code Online (Sandbox Code Playgroud)

在其他地方是一个方法,Feed,它需要一个哺乳动物,从那里我想在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.
}
Run Code Online (Sandbox Code Playgroud)

一种方法是做类似的事情:

public void Feed(Mammal mammal) {
    if (mammal is dog) 
        AnotherClass.GiveFood((Dog)mammal);
    if (mammal is Cat) 
        AnotherClass.GiveFood((Cat)mammal);
}
Run Code Online (Sandbox Code Playgroud)

......但实际上我有很多来自哺乳动物的动物.有没有更好的方法在Feed()中做我想做的事情?有没有什么方法可以避免Feed()最终成为一个巨大的丑陋方法填充这些"如果x是y然后调用z" - 陈述?

Jer*_*odd 8

我通常不喜欢使用dynamic,但这是我认为合适的情况之一:

public void Feed(Mammal mammal) {
  Anotherclass.GiveFood((dynamic)mammal);
}
Run Code Online (Sandbox Code Playgroud)

这将在运行时解决正确的重载,而无需事先知道类型.

严格地说,这可能不是最快的方法,但正如你所指出的,替代方案可能是维护和/或难以阅读的真正痛苦.在这种情况下,动态调度是优雅的,并将自动合并您将来添加的任何重载.

正如克里斯·辛克莱指出,你还可以添加一个包罗万象的方法来检测任何无效的调用,并提供比你收到,如果没有匹配的运行时错误更友好的异常GiveFood()过载可以发现:

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 + ".");
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 嗯,是的,如果应用程序没有设置为正确处理所有有效的情况,它将抛出,但如果是这种情况,那么你似乎很可能会遇到问题.我可以看到关于抛出一个更友好的异常的观点,但问题仍然需要以某种方式纠正.也许这只是我,但这种方法的优雅和可维护性超过了缺点. (2认同)

Cod*_*ter 5

我认为处理食物是动物的责任,而不是饲养者.否则你会遇到你现在遇到的问题:

public void Feed(Mammal mammal) {
    if (mammal is Duck) 
    {
        ((Duck)mammal).PryOpenBeak();
        ((Duck)mammal).InsertFeedingTube();
        ((Duck)mammal).PourDownFood();
    }
}
Run Code Online (Sandbox Code Playgroud)

等等,虽然鸭子不是哺乳动物.

无论如何,你的Mammal 班级应该有一个抽象的方法Feed(Food food),动物本身必须弄清楚如何处理食物.这样,当后来添加一种新的哺乳动物时,您不必使用这种新哺乳动物的喂养逻辑来更新喂食器.

@Chris的评论:然后动物可以实现IFoodXEater包含Feed(IFoodX)方法的正确接口,然后接收器可以查看它,尽管那时你回到了正方形:

if (mammal is IFishEater)
{
    ((IFishEater)mammal).Feed(new Fish());
}
Run Code Online (Sandbox Code Playgroud)