简体   繁体   English

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

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

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()? 有没有更好的方法在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? 有没有什么方法可以避免Feed()最终成为一个巨大的丑陋方法填充这些“如果x是y然后调用z” - 陈述?

I don't usually like using dynamic , but this is one of the cases where I think it's appropriate: 我通常不喜欢使用dynamic ,但这是我认为合适的情况之一:

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

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. 无论如何,你的Mammal类应该有一个抽象的方法Feed(Food 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: IFoodXEater的评论:然后动物可以实现包含Feed(IFoodX)方法的正确IFoodXEater接口,然后接收器可以查看它,尽管那时你回到了方形一:

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

If you don't mind the effort of creating a type map, you can fake double dispatch like so: 如果您不介意创建类型映射的工作,您可以伪造双重调度,如下所示:

[EDIT] This new, improved version handles subclasses better. [编辑]这个新的改进版本更好地处理子类。 If you have a class derived from another mammal class (such as Pug derived from Dog in the example below) then you don't need to explicitly add a feeder for class Pug - it will automatically call the feeder for its base class, Dog . 如果你有一个派生自另一个哺乳动物类的类(例如下面例子中派生自Dog Pug ),那么你不需要为Pug类明确添加一个馈线 - 它会自动调用它的基类Dog的馈线。

But you can have a specific feeder for a derived class if you want, as demonstrated by the Manx class below. 但是,如果需要,您可以为派生类创建特定的进纸器,如下面的Manx类所示。

Using dynamic is much much easier though! 使用dynamic虽然容易得多! I just wanted to show how it could look if you weren't using 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();
        }
    }
}

My Recommendation: 我的建议:

Step 1 : Create an interface IMammal 第1步 :创建一个接口IMammal

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

Step 2 : (Optional) Implement a Base class BaseMammal 第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
    }
}

Step 3 : Implement your inherited classes 第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
    }
}

Step 4 : Use! 第4步 :使用! (random example included) (包括随机示例)

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

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

Each animal will be fed by their own implementation. 每只动物都将通过自己的实施方式喂养。 Lo and behold - your complex code is now simple. 瞧,你的复杂代码现在很简单。 I would also recommend that you read "Head First Design Patterns", which explains this and many other concepts. 我还建议你阅读“Head First Design Patterns”,它解释了这个和许多其他概念。 http://www.amazon.co.uk/Head-First-Design-Patterns-Freeman/dp/0596007124 http://www.amazon.co.uk/Head-First-Design-Patterns-Freeman/dp/0596007124

If more than one animal shares the feeding behavior, I'll suggest to use the strategy pattern to encapsulate the feeding behavior in an interface and concrete implement each behavior for each group of animals 如果不止一只动物分享喂养行为,我建议使用策略模式将喂养行为封装在界面中并具体实现每组动物的每种行为

you will be using composition instead of inheritance 你将使用组合而不是继承

check the head first design patterns for this one I think it will be a good implementation in your case 检查头部的第一个设计模式我认为这将是一个很好的实现在你的情况下

暂无
暂无

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

相关问题 如何从基类调用派生类方法? - How do I call a derived class method from the base class? C#:如何从派生类的静态方法调用基类的静态方法? - C#: How do I call a static method of a base class from a static method of a derived class? 我可以调用基类的派生方法吗? - Can I call a derived method from base class? 如何获取派生方法来更改其基本变量或对象之一,然后从类外部调用更改的对象? - How do I get a derived method to change one of its base variables or objects and then call the changed object from outside the class? 我似乎无法将类中的方法调用到另一个类中。 我的主类和ApplicationUtilities类有错误。 我究竟做错了什么? - I can't seem to call a method in class into another class. I have errors on my Main Class and my ApplicationUtilities class. What am I doing wrong? 基类=新派生类。 这是如何运作的? - Base Class = New Derived Class. How does this work? 我应该如何使用从通用基础 class 派生的 class 调用基础 class 构造函数? - How should I call a base class constructor with a class derived from a generic base class? 派生类不调用基类方法 - Derived class does not call base class method 我如何将类(派生自通用“基”类)转换为该通用“基”类 - How do i convert a class (which is derived from a generic "base" class) to that generic "base" class 如何使用NHibernate在基类上映射派生属性? - How do I map a derived property on a base class using NHibernate?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM