c#中的多态向下转换?

时间:2014-09-13 19:00:31

标签: c# polymorphism downcast

我正在尝试使用IAnimal为我的FoodFactory制作食物,并List<IAnimal> DogFox等。IAnimal理想情况下,我会传递一个FoodFactoryinterface IAnimal { void MakeNoise(); } public sealed class Dog : IAnimal { public void MakeNoise() { Console.WriteLine("Woof woof"); } } public sealed class Fox : IAnimal { public void MakeNoise() { Console.WriteLine("What does the fox say?"); } } public static class FoodFactory { public static void Create(Dog dog) { Console.WriteLine("return dog food"); } public static void Create(Fox fox) { Console.WriteLine("return fox food"); } } 会返回正确的食物,但我不知道该怎么做。

var myList = new List<IAnimal> { new Dog(), new Fox() };
foreach (var animal in myList)
{
    FoodFactory.Create(animal); // Sadly, this doesn't work
}

这是我的来电代码:

if (animal is Dog) { FoodFactory.Create(animal as Dog); }

有没有“正确”的方法来做到这一点?我能想到的只是if语句的加载:

{{1}}

我有很多动物,而且我宁愿计算机解决它所处理的问题而不是我!谢谢。

更新

感谢所有发布的内容。我选择了新潮的答案,因为它最适用于我的背景;它没有影响我的动物类,并且允许在运行时解析类型而不检查所有动物类型。有三个有用的答案可以使用(例如使用访客模式),但它们在我的背景下不太适用,因为我不想改变/扩展我的动物。

4 个答案:

答案 0 :(得分:4)

您遇到此问题的原因是所谓的重载解析在编译时发生,而不是在运行时发生。 有几种方法可以解决它: 使用一堆if-else-if语句肯定是一种方法,尽管你可以把它写得更漂亮。

使用.Net 4.0或更高版本,您可以使用&#34; dynamic&#34;用于将重载决策延迟到运行时的关键字。它会降低性能,但它可以准确地实现您的需求:

public static class FoodFactory {
     public static void Create(dynamic animal) {
        InternalCreate (animal);
    }
    private static void InternalCreate (Dog dog) {
        Console.WriteLine("return dog food");
    }
    private static void InternalCreate (Fox fox) {
        Console.WriteLine("return fox food");
    }
}

要添加更多类型安全性,您可以考虑执行以下操作:

public static class FoodFactory {
    public static void Create(IAnimal animal) {
        Dispatch (animal);
    }

    private static void Dispatch (dynamic animal) {
        InternalCreate (animal);
    }
    private static void InternalCreate (Dog dog) {
        Console.WriteLine("return dog food");
    }
    private static void InternalCreate (Fox fox) {
        Console.WriteLine("return fox food");
    }
}

答案 1 :(得分:2)

我认为您需要修改FoodFactory类中的Create方法,以接受IAnimal作为参数。你还需要有一个FavFood的方法声明或者你想在接口中调用它的任何东西,它将从继承接口的类(Dogs,Fox)获得特定的实现。然后在工厂类中,您可以提供任何IAnimal类型的动物,并且有一个方法可以调用Animal特定的FavFood方法。

interface IAnimal
{
    void FavFood();
}

public sealed class Dog : IAnimal
{
    public void FavFood() { Console.WriteLine("Me loves  dog food. Woof woof."); }
}

public sealed class Goat : IAnimal
{
    public void FavFood() { Console.WriteLine("Me loves goat food. myaaa.."); }
}


public static class FoodFactory
{
    public static void Create(IAnimal animal)
    {
        animal.FavFood();
    }
}

当你打电话时,你会这样做

var animals = new List<IAnimal> { new Dog(), new Fox() };

foreach (var animal in animals)
{
    FoodFactory.Create(animal);
}

我是否回答了您的要求?如果没有,请告诉我错过的地方......

答案 2 :(得分:2)

你的FoodFactory确实需要知道喂食什么样的动物,但我会想象动物本身知道它想要吃什么样的食物。如果IAnimal提供了该怎么办?

interface IAnimal {
   void MakeNoise();
   FoodType FavoriteFood { get; }
}
public sealed class Dog : IAnimal {
    public void MakeNoise() { Console.WriteLine("Woof woof"); }
    public FoodType FavoriteFood { get { return FoodType.DogFood; } }
}
public sealed class Fox : IAnimal {
    public void MakeNoise() { Console.WriteLine("What does the fox say?"); }
    public FoodType FavoriteFood { get { return FoodType.WhatTheFoxEats; } }
}

现在,您的FoodFactory拥有制作食物所需的信息:

public static class FoodFactory {
    public static IFood Create(IAnimal animal) {
        // animal.FavoriteFood tells you what food to make
    }
}

也许FavoriteFood实际上会返回一个食物实例,而工厂只是代表那个?也许工厂维护上面enum的映射或其他逻辑到实际的食物实例?也许其他一些方式? (或者你可能只是按照你已经输出到控制台而不是像我想要的那样返回一个对象,在这种情况下,动物只能写入控制台,工厂仍然不需要知道任何其他关于动物。)无论如何,工厂与动物的关系现在已经多态化了。

答案 3 :(得分:2)

另一种选择是使用访客模式:

using System;
using System.Collections.Generic;

public interface IAnimalVisitor
{
    void Visit(Dog dog);
    void Visit(Fox fox);
}

public class FeedingPersonnel : IAnimalVisitor
{
    public void Visit(Dog dog) 
    {
        Console.WriteLine("return dog food");
    }

    public void Visit(Fox fox)
    {
        Console.WriteLine("return fox food");
    }
}

//public class Veterinarian : IAniamalVisitor { ... } (if you need it)

interface IAnimal 
{
    void Accept( IAnimalVisitor visitor);
    void MakeNoise();
}

public sealed class Dog : IAnimal {
    public void Accept( IAnimalVisitor visitor) { visitor.Visit(this); }
    public void MakeNoise() { Console.WriteLine("Woof woof"); }
}

public sealed class Fox : IAnimal {
    public void Accept( IAnimalVisitor visitor) { visitor.Visit(this); }
    public void MakeNoise() { Console.WriteLine("What does the fox say?"); }
}


public class Test
{
    public static void Main()
    {
        var myList = new List<IAnimal> { new Dog(), new Fox() };
        var feeder = new FeedingPersonnel();

        foreach (var animal in myList)
        {
           animal.Accept(feeder);
        }
    }
}

此方法可避免投射,还可为您提供添加其他类型访问者的方法。您必须为每个新Animal提供一次访问权限,并向IAnimalVisitor添加一个新方法,并在每次添加一个新Animal时在每个访问者实现者中定义它。

根据您的实际情况,这可能是可以接受的(或不是)。