Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何改变这种设计以避免沮丧?_C#_Design Patterns_Overloading_Downcast - Fatal编程技术网

C# 如何改变这种设计以避免沮丧?

C# 如何改变这种设计以避免沮丧?,c#,design-patterns,overloading,downcast,C#,Design Patterns,Overloading,Downcast,假设我有一个从基类继承的对象集合。类似于 abstract public class Animal { } public class Dog :Animal { } class Monkey : Animal { } 现在,我们需要给这些动物喂食,但他们不被允许知道如何喂食自己。如果可以,答案很简单: foreach( Animal a in myAnimals ) { a.feed(); } 然而,他们不

假设我有一个从基类继承的对象集合。类似于

   abstract public class Animal
    {

    }

    public class Dog :Animal
    {

    }

    class Monkey : Animal
    {

    }
现在,我们需要给这些动物喂食,但他们不被允许知道如何喂食自己。如果可以,答案很简单:

foreach( Animal a in myAnimals )
{
   a.feed();
}
然而,他们不知道如何养活自己,所以我们想做如下事情:

    class Program
{
    static void Main(string[] args)
    {
        List<Animal> myAnimals = new List<Animal>();

        myAnimals.Add(new Monkey());
        myAnimals.Add(new Dog());

        foreach (Animal a in myAnimals)
        {
            Program.FeedAnimal(a);
        }
    }

    void FeedAnimal(Monkey m) {
        Console.WriteLine("Fed a monkey.");
    }

    void FeedAnimal(Dog d)
    {
        Console.WriteLine("Fed a dog.");
    }

}
类程序
{
静态void Main(字符串[]参数)
{
List myAnimals=新列表();
添加(新猴子());
添加(新狗());
foreach(我的动物中的动物a)
{
饲养动物计划(a);
}
}
空饲动物(猴子m){
Console.WriteLine(“喂猴子”);
}
空饲动物(狗d)
{
Console.WriteLine(“喂狗”);
}
}
当然,这不会编译,因为它会强制向下转换

感觉上好像有一个设计模式或其他一些泛型解决方案可以帮助我解决这个问题,但我还没有把我的手指放在它上面


建议?

在Linq习惯用法中使用的选中向下广播是完全安全的

foreach (Monkey m in myAnimals.OfType<Monkey>())
    Program.FeedAnimal(m);
要注册馈送操作,首先要执行以下操作:

_feeders[typeof(Monkey)] = 
    a =>
    {
        Monkey m = (Monkey)a;

        // give food to m somehow
    };
但是有一个缺点,你必须在键中输入正确的类型。因此,做一个助手:

public static void AddFeeder<TAnimal>(Action<TAnimal> feeder) where TAnimal : Animal
{
    _feeders[typeof(TAnimal)] = a => feeder((TAnimal)a);
}
e、 g

因此_feeders将是一个私有静态字段,与扩展方法以及
AddFeeder
方法位于同一个静态类中

更新:将所有代码放在一个地方,并支持继承:

public static class AnimalFeeding
{
    private static Dictionary<Type, Action<Animal>> _feeders 
        = new Dictionary<Type, Action<Animal>>();

    public static void AddFeeder<TAnimal>(Action<TAnimal> feeder) 
        where TAnimal : Animal
    {
        _feeders[typeof(TAnimal)] = a => feeder((TAnimal)a);
    }

    public static void Feed(this Animal a)
    {
        for (Type t = a.GetType(); t != null; t = t.BaseType)
        {
            Action<Animal> feeder;
            if (_feeders.TryGetValue(t, out feeder))
            {
                feeder(a);
                return;
            }
        }

        throw new SystemException("No feeder found for " + a.GetType());
    }
}
公共静态类动物饲料
{
专用静态字典馈线
=新字典();
公共静态无效添加馈线(动作馈线)
塔尼马尔:动物
{
_馈线[typeof(TAnimal)]=a=>馈线((TAnimal)a);
}
公共静态无效饲料(该动物a)
{
for(类型t=a.GetType();t!=null;t=t.BaseType)
{
动作给料机;
if(_馈线TryGetValue(t,输出馈线))
{
馈线(a);
返回;
}
}
抛出新的SystemException(“未找到“+a.GetType()的馈送器”);
}
}

您还可以通过每种类型支持的接口进行循环
t
——基本上是相同的想法,因此我在这里保持了示例的简单性。

由于动物不允许自己或其他动物进食,因此您没有其他选项来创建类“所有者”或“看守者”或“看守者”,谁拥有私人饲养动物的方法。

这是OOD中的一个经典问题。如果类(动物)集合是固定的,则可以使用。如果您的一组操作(例如feed)是有限的,您只需向Animal添加一个feed()方法。如果这些都不成立,就没有简单的解决办法。

泛型只有在你有一个“狗的列表”并且想要用一个参数调用一个方法,这个参数是“动物的事物列表”(即
列表,其中T:Animal
)-我认为它在这里没有帮助


我想你需要一个。。。一些对象集可能知道如何喂养给定类型的动物,并不断尝试它们,直到找到一个知道如何喂养的对象。

您可以在动物类中包含一个成员变量,该变量将标识动物的类型,然后让feed函数读取它并根据它生成不同的结果。

首先,面向对象设计的一个要点是,对象将其数据与作用于该数据的行为捆绑在一起(即,“动物知道如何养活自己”)。所以这是一种“医生,当我这样做时会很痛!-所以不要那样做”的情况

也就是说,我相信这个故事比你描述的要多,你有很好的理由不能做“适当的”OOD。所以你有几个选择

您可以让您的FeedAnimal(Animal a)方法使用反射来查找动物的类型。基本上你是在用饲养动物的方法进行多态性研究

static void FeedAnimal(Animal a)
{
    if (a is Dog)
    {
        Console.WriteLine("Fed a dog.");
    }
    else if (a is Monkey)
    {
        Console.WriteLine("Fed a monkey.");
    }
    else
    {
        Console.WriteLine("I don't know how to feed a " + a.GetType().Name + ".");
    }      
}

一种更面向对象但更复杂的方法是使用其他人建议的访问者模式。对于有经验的开发人员来说,这更优雅,但对于更多的新手程序员来说,可能不那么明显和可读。你喜欢哪种方法可能取决于你有多少种不同的动物类型。

很想听听你对我的回答的意见,Marc。(自被要求后)类型的
很好,但你必须拥有与你的品种一样多的
类型的
,因此最终成为探视。这里讨论的基于字典/委托的访问者实现是许多有效方法中的一种,但需要考虑一下继承……即。如果我为
Dog
注册了一个操作,但添加了一个
Collie
实例,它就会崩溃。此外,lambda可以简化(我认为)为AddFeeder(GiveBananasTo);添加馈线(ThrowBiscuitsAt)。而是合理的讨论-=p我真正提到的类型是为了说明,只要你将沮丧情绪封装起来以确保其安全,它就是好的,这是对其余答案的介绍。@Jesse,如果你想举一个例子说明为什么狗不应该自己进食,那么狗吃太多巧克力是有毒的。
public static void Feed(this Animal a)
{
    _feeders[a.GetType()](a);
}
a.Feed();
public static class AnimalFeeding
{
    private static Dictionary<Type, Action<Animal>> _feeders 
        = new Dictionary<Type, Action<Animal>>();

    public static void AddFeeder<TAnimal>(Action<TAnimal> feeder) 
        where TAnimal : Animal
    {
        _feeders[typeof(TAnimal)] = a => feeder((TAnimal)a);
    }

    public static void Feed(this Animal a)
    {
        for (Type t = a.GetType(); t != null; t = t.BaseType)
        {
            Action<Animal> feeder;
            if (_feeders.TryGetValue(t, out feeder))
            {
                feeder(a);
                return;
            }
        }

        throw new SystemException("No feeder found for " + a.GetType());
    }
}
static void FeedAnimal(Animal a)
{
    if (a is Dog)
    {
        Console.WriteLine("Fed a dog.");
    }
    else if (a is Monkey)
    {
        Console.WriteLine("Fed a monkey.");
    }
    else
    {
        Console.WriteLine("I don't know how to feed a " + a.GetType().Name + ".");
    }      
}