如何允许类属性在c#中具有多种/灵活类型?

时间:2010-09-07 17:08:42

标签: c# polymorphism

在C#中我有三个类:Person,Cat和Dog。

Cat和Dog类都有Eat()方法。

我希望Person类拥有属性'Pet'。

我希望能够通过Person通过类似Person.Pet.Eat()的方式调用Cat和Dog的Eat方法,但我不能,因为Pet属性需要是Cat或Dog类型。

目前我正在使用Person类中的两个属性:PetDog和PetCat。

现在这没关系,但如果我想要100种不同类型的动物作为宠物,那么我真的不想在Person类中拥有100种不同的宠物属性。

使用Interfaces或Inheritance有没有办法解决这个问题?有没有办法可以将Pet设置为Object类型,但仍然可以访问分配给它的动物类的属性?

6 个答案:

答案 0 :(得分:12)

你可以让宠物来自一个共同的基类:

public abstract class Animal
{
    protected Animal() { }

    public abstract void Eat();
}

然后Cat和Dog派生于这个基类:

public class Cat: Animal
{
    public override void Eat()
    {
        // TODO: Provide an implementation for an eating cat
    }
}

public class Dog: Animal
{
    public override void Eat()
    {
        // TODO: Provide an implementation for an eating dog
    }
}

您的Person类将具有Animal类型的属性:

public class Person
{
    public Animal Pet { get; set; }
}

当你有一个Person实例时:

var person = new Person 
{
    Pet = new Cat()
};
// This will call the Eat method from the Cat class as the pet is a cat
person.Pet.Eat();

您还可以为基类中的Eat方法提供一些常用实现,以避免在派生类中重写它:

public abstract class Animal
{
    protected Animal() { }

    public virtual void Eat()
    {
        // TODO : Provide some common implementation for an eating animal
    }
}

请注意,Animal仍然是一个抽象类,以防止它直接实例化。

public class Cat: Animal
{
}

public class Dog: Animal
{
    public override void Eat()
    {
        // TODO: Some specific behavior for an eating dog like
        // doing a mess all around his bowl :-)

        base.Eat();
    }
}

答案 1 :(得分:11)

  

使用Interfaces或Inheritance有没有办法解决这个问题?

接口:创建一个接口IEat或IPet或您想要表示的任何概念。使界面具有Eat方法。让Cat和Dog实现此界面。让Pet属性属于那种类型。

继承:创建一个抽象基类Animal或Pet或您想要表示的任何概念。在基类上创建一个抽象方法Eat。让猫和狗继承这个基类。让Pet属性属于那种类型。

这两者有什么区别?

使用接口来模拟“X知道如何做Y”的想法。例如,IDisposable意味着“我知道如何处置我所持有的重要资源”。这不是关于的对象的事实,而是关于做什么的事实。

使用继承来模拟“X是一种Y”的概念。狗是一种动物。

关于接口的事情是你可以拥有任意数量的接口。但是你只能直接从一个基类继承,所以如果你要使用继承,你必须确保你做对了。继承的问题是人们最终制作像“车辆”这样的基础类,然后他们说“一辆军车是一种车辆”和“一艘船是一种车辆”,现在你被卡住了:什么是基础毁灭者的类?它既是Ship又是MilitaryVehicle,它不能同时是非常谨慎地选择“继承支点”

  

有没有办法可以将Pet设置为Object类型,但仍然可以访问分配给它的动物类的属性?

是的,在C#4中有,但不这样做。使用接口或继承。

在C#4中,您可以使用“dynamic”在运行时动态调度到Pet中对象的Eat方法。

你不想这样做的原因是因为如果有人将水果或手锯放在宠物财产中然后尝试让它吃掉,这会崩溃并死得很厉害。编译时检查的目的是减少程序的脆弱性。如果您有办法进行更多的编译时检查,请使用它。

答案 2 :(得分:1)

使用界面;

abstract class Animal
{
    public virtual void DoSomething() { }
}

interface ICanEat
{
    void Eat();
}

class Dog : Animal, ICanEat
{
    public void Eat()
    {
        Console.Out.WriteLine("Dog eat");
    }
}

class Cat : Animal, ICanEat
{
    public void Eat()
    {
        Console.Out.WriteLine("Cat eat");
    }
}

class Person
{
    public Animal MyAnimal { get; set; }
}

然后你可以打电话

(person.MyAnimal as ICanEat).Eat();

随时执行正确的空检查。

答案 3 :(得分:1)

使用抽象类。

public abstract class Pet { public abstract void Eat(); }

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

public class Person {
    public Pet Pet;
}

我相信你可以做其余的事。

答案 4 :(得分:0)

为什么不创建Pet

类型的基类
public abstract class Pet{

    public abstract void Eat();

}

答案 5 :(得分:0)

让这只猫和狗继承自界面IPet

public interface IPet
{
    bool hungry();
    void Eat();
}