繁体   English   中英

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

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

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

public class Mammal { }

public class Cat : Mammal { } 

public class Dog : Mammal { }

和实用类:

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

在其他地方是一个方法,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.
}

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

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

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

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

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

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

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

正如Chris Sinclair指出的那样,如果找不到匹配的GiveFood()重载,你还可以添加一个catchall方法来检测任何无效调用,并提供比你收到的运行时错误更友好的异常:

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 + ".");
  }
}

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

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

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

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

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

if (mammal is IFishEater)
{
    ((IFishEater)mammal).Feed(new Fish());
}

如果您不介意创建类型映射的工作,您可以伪造双重调度,如下所示:

[编辑]这个新的改进版本更好地处理子类。 如果你有一个派生自另一个哺乳动物类的类(例如下面例子中派生自Dog Pug ),那么你不需要为Pug类明确添加一个馈线 - 它会自动调用它的基类Dog的馈线。

但是,如果需要,您可以为派生类创建特定的进纸器,如下面的Manx类所示。

使用dynamic虽然容易得多! 我只是想展示一下如果你没有使用dynamic

using System;
using System.Collections.Generic;

namespace Demo
{
    public class Mammal {}

    public class Cat: Mammal {}
    public class Pig: Mammal {}
    public class Dog: Mammal {}

    public class Pug:  Dog {}
    public class Manx: Cat {}

    public static class Feeder
    {
        static readonly Dictionary<Type, Action<Mammal>> map = createMap();

        static Dictionary<Type, Action<Mammal>> createMap()
        {
            return new Dictionary<Type, Action<Mammal>>
            {
                {typeof(Cat),  mammal => GiveFood((Cat)  mammal)},
                {typeof(Dog),  mammal => GiveFood((Dog)  mammal)},
                {typeof(Manx), mammal => GiveFood((Manx) mammal)}
            };
        }

        public static void GiveFood(Mammal mammal)
        {
            for (
                var currentType = mammal.GetType(); 
                typeof(Mammal).IsAssignableFrom(currentType);
                currentType = currentType.BaseType)
            {
                if (map.ContainsKey(currentType))
                {
                    map[currentType](mammal);
                    return;
                }
            }

            DefaultGiveFood(mammal);
        }

        public static void DefaultGiveFood(Mammal mammal)
        {
            Console.WriteLine("Feeding an unknown mammal.");
        }

        public static void GiveFood(Cat cat)
        {
            Console.WriteLine("Feeding the cat.");
        }

        public static void GiveFood(Manx cat)
        {
            Console.WriteLine("Feeding the Manx cat.");
        }

        public static void GiveFood(Dog dog)
        {
            Console.WriteLine("Feeding the dog.");
        }
    }

    class Program
    {
        void test()
        {
            feed(new Cat());
            feed(new Manx());
            feed(new Dog());
            feed(new Pug());
            feed(new Pig());
            feed(new Mammal());
        }

        void feed(Mammal mammal)
        {
            Feeder.GiveFood(mammal);
        }

        static void Main()
        {
            new Program().test();
        }
    }
}

我的建议:

第1步 :创建一个接口IMammal

<!-- language: c# -->
public interface IMammal
{
    void Feed();
}

第2步 :(可选)实现Base类BaseMammal

public class BaseMammal : IMammal
{
    public void Feed()
    {
        Trace.Write("basic mammal feeding");
        //a basic implementation of feeding, common to all or most mammals
    }
}

第3步 :实现继承的类

public class Cat : BaseMammal
{
    public void Feed()
    {
        Trace.Write("cat feeding");
        BePicky();//some custom cat like functionality
        base.Feed(); //and afterwards its still just a mammal after all
    }
}

public class Gruffalo : BaseMammal
{
    public void Feed()
    {
        Trace.Write("Gruffalo feeding");
        WeirdWayOfEating();//the base implementation is not appropriate
    }
}

第4步 :使用! (包括随机示例)

List<IMammal> pets = new List<IMammal>()
    {
        new Cat(catValues),
        new Gruffalo(gruffaloValues)
    };

foreach(var pet in pets)
{
    pet.Feed();
}

每只动物都将通过自己的实施方式喂养。 瞧,你的复杂代码现在很简单。 我还建议你阅读“Head First Design Patterns”,它解释了这个和许多其他概念。 http://www.amazon.co.uk/Head-First-Design-Patterns-Freeman/dp/0596007124

如果不止一只动物分享喂养行为,我建议使用策略模式将喂养行为封装在界面中并具体实现每组动物的每种行为

你将使用组合而不是继承

检查头部的第一个设计模式我认为这将是一个很好的实现在你的情况下

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM