如何在函数中避免过多的向下转换

时间:2013-04-24 15:05:01

标签: c# templates downcast

我的代码中有太多的向下转发。在c ++中,我可以使用模板来避免向下转换。但是c#中的以下示例的最佳实现是什么?

class Pet { bool mIsDog; }
class Dog : Pet // mIsDog = true
class Cat : Pet // mIsDog = false

class Owner //There are many different owner classes, may come from different dlls
{
    private List<Food> mFoods;

    private void FeedDog(Dog dog)
    {
        ... //Use mFoods, which is invisible to dog
    }

    void HandlePet(Pet pet)
    {
        if(pet.mIsDog) //In c++ I can use templates to avoid downcasting
            FeedDog((Dog)pet);
        else
            FeedCat((Cat)pet);

        ... //code handling general pet

        while(pet.IsUnhappy())
        {
            if(pet.mIsDog) //In c++ I can use templates to avoid downcasting
                PlayWithDog((Dog)pet);
            else
                PlayWithCat((Cat)pet);
        }

        ... //a lot of code handling general pet or specific pet (cat or dog)
    }
}

请注意,函数HandlePet具有非常复杂的逻辑,具有多级缩进,因此很难将其分解为多个单独的函数。


我没有在Pet类中使用BeFedWith或BePlayedWith虚拟函数的原因是我可能有许多不同的所有者类,例如BoyOwner,GirlOwner,WomanOwner,ManOwner,每个类都有自己的方式来喂养宠物。 Pet是一个常见的类,并被许多其他类使用,但Owner类只与Pet交互。此外,FeedDog等函数需要访问Owner类的私有成员。

3 个答案:

答案 0 :(得分:1)

您尝试解决的问题是如何根据对象类型进行调度,当您在设计对象时不知道可能需要的所有不同操作。 (其他答案建议将所有必需的操作放在基类中;这有时是不可能的,并导致过度耦合)。

答案不是测试对象的类型和强制转换;如果有的话,你应该很少这样做。一个好的解决方案是使用Visitor pattern:当你使对象可访问时,可以通过实现操作来添加在对象类型上调度的新操作,而不是通过更改对象本身。

答案 1 :(得分:0)

嗯,向下倾斜被认为是糟糕的设计...... 你应该将常用方法移动到baseclass并使它们抽象或有一个接口来实现它。

如果你有:

public interface Pet{
    void Feed();
    void Play();
}

public class Dog:Pet{
    public void Feed(){
        ...dog feeding code...
    }
    public void Play(){
        ...dog playing code...
    }
}
public class Cat:Pet{
    public void Feed(){
        ...cat feeding code...
    }
    public void Play(){
        ...cat playing code...
    }
}

然后你handlePet函数变为:

void HandlePet(Pet pet){
    pet.Feed();
    while(pet.isUnhappy())
        pet.Play();
}

答案 2 :(得分:0)

如果你必须按照你的建议行事,保持Feed等方法属于所有者而不是宠物,那么你可以利用动态绑定来调用正确的所有者方法:

using System;

class Pet { }
class Dog : Pet { }
class Cat : Pet { }

class Owner
{
    public void HandlePet(Pet pet)
    {
        HandlePet((dynamic)pet);
    }

    private void HandlePet(Cat pet)
    {
        Console.WriteLine("Stroke the cat softly whispering Please don't scratch me.");
    }

    private void HandlePet(Dog pet)
    {
        Console.WriteLine("Stroke the dog firmly saying Who's a good boy?");
    }
}

class Program
{
    static void Main(string[] args)
    {
        var owner = new Owner();

        Console.WriteLine("Handle dog first");
        owner.HandlePet(new Dog());

        Console.WriteLine("Handle cat second");
        owner.HandlePet(new Cat());

        Console.ReadKey();
    }
}

输出:

  

首先处理狗   坚定地说狗谁是个好男孩?   处理猫秒   轻轻地抚摸着猫,请不要刮我。


如果你可以避免它,那么我会把方法放在Pet类上,如下所示:
一种方法是使Pet成为一个抽象类,它具有EatFoodPlay抽象方法。 DogCat类将实现这些方法:

using System;

abstract class Pet
{
    public abstract void EatFood();
    public abstract void Play();

    public void BrushHair()
    {
        Console.WriteLine("Hair brushed!");
    }
}
class Dog : Pet
{
    public override void EatFood()
    {
        Console.WriteLine("Eating dog food...");
    }

    public override void Play()
    {
        Console.WriteLine("Run around manically barking at everything.");
    }
}
class Cat : Pet
{
    public override void EatFood()
    {
        Console.WriteLine("Eating cat food...");
    }

    public override void Play()
    {
        Console.WriteLine("Randomly choose something breakable to knock over.");
    }
}

class Owner
{
    public void HandlePet(Pet pet)
    {
        pet.EatFood();
        pet.Play();
        pet.BrushHair();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var owner = new Owner();

        Console.WriteLine("Handle dog first");
        owner.HandlePet(new Dog());

        Console.WriteLine("Handle cat second");
        owner.HandlePet(new Cat());

        Console.ReadKey();
    }
}

输出:

  

首先处理狗   吃狗粮...
  对所有事情都狂奔吠叫   刷头发!
  处理猫秒   吃猫粮...
  随机选择易碎的东西来敲门   头发刷了!


如果您想使用访客模式,请尝试以下代码:

using System;

public interface IPetVisitor
{
    void Visit(Dog dog);
    void Visit(Cat cat);
}

public interface IPetAcceptor<T>
{
    void Accept(T visitor);
}

public abstract class Pet : IPetAcceptor<IPetVisitor>
{
    public abstract void Accept(IPetVisitor visitor);
}
public class Dog : Pet
{
    public override void Accept(IPetVisitor visitor)
    {
        visitor.Visit(this);
    }
}
public class Cat : Pet
{
    public override void Accept(IPetVisitor visitor)
    {
        visitor.Visit(this);
    }
}

class Owner
{
    // Private variable on owner class
    private int HandCount = 2;

    // Pet handler is an inner class so we can access the enclosing class' private member.
    public class PetHandler : IPetVisitor
    {
        private Owner Owner;

        public PetHandler(Owner owner)
        { Owner = owner; }

        public void Visit(Dog dog)
        {
            Console.WriteLine("Pet the dog with {0} hands", Owner.HandCount);
        }

        public void Visit(Cat cat)
        {
            Console.WriteLine("Pet the cat with {0} hands", Owner.HandCount);
        }
    }
    private PetHandler PetHandlerInstance;

    public Owner()
    {
        PetHandlerInstance = new PetHandler(this);
    }

    public void HandlePet(IPetAcceptor<IPetVisitor> pet)
    {
        pet.Accept(PetHandlerInstance);
    }
}

class Program
{
    static void Main(string[] args)
    {
        var owner = new Owner();

        Console.WriteLine("Handle dog first");
        owner.HandlePet(new Dog());

        Console.WriteLine("Handle cat second");
        owner.HandlePet(new Cat());

        Console.ReadKey();
    }
}