繁体   English   中英

c#中的多态向下转换?

[英]Polymorphic Downcasting in c#?

我正在尝试使用FoodFactory为我的IAnimal做食物,并且有一个大的List<IAnimal>DogFox等。理想情况下,我会传入一个IAnimalFoodFactory将返回正确的食物,但我不是确定如何做到这一点。

interface 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语句的加载:

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

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

UPDATE

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

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

使用.Net 4.0或更高版本,您可以使用“dynamic”关键字将重载决策延迟到运行时。 它有性能损失,但它可以准确地实现您的需求:

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

我认为您需要修改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);
}

我回答你在找什么吗? 如果没有,请告诉我错过的地方......

你的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的映射或其他逻辑维护到实际的食物实例中? 也许其他一些方式? (或者你可能只是按照你已经输出到控制台而不是像我想要的那样返回一个对象,在这种情况下,动物只能写入控制台,工厂仍然不需要知道任何其他关于在任何情况下,工厂与动物的关系现在都是多态定义的。

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

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时在每个访问者实现者中定义它。

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

暂无
暂无

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

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