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

如何在c#中允许类属性具有多种/灵活的类型?,c#,polymorphism,C#,Polymorphism,在C#中,我有三类:人、猫和狗 Cat和Dog类都有Eat()方法 我希望Person类有一个属性“Pet” 我希望能够通过Person调用Cat和Dog的Eat方法,比如Person.Pet.Eat(),但是我不能,因为Pet属性需要是Cat或Dog类型 目前,我在Person类中使用了两个属性:PetDog和PetCat 现在这还可以,但是如果我想要100种不同类型的动物作为宠物,那么我真的不想在Person类中有100种不同的宠物属性 有没有办法使用接口或继承来解决这个问题?是否有一种方法

在C#中,我有三类:人、猫和狗

Cat和Dog类都有Eat()方法

我希望Person类有一个属性“Pet”

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

目前,我在Person类中使用了两个属性:PetDog和PetCat

现在这还可以,但是如果我想要100种不同类型的动物作为宠物,那么我真的不想在Person类中有100种不同的宠物属性

有没有办法使用接口或继承来解决这个问题?是否有一种方法可以将宠物设置为Object类型,但仍然可以访问分配给它的任何动物类别的属性?

使用接口

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();
执行正确的空检查。

使用抽象类

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

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

public class Person {
    public Pet Pet;
}

我相信您可以完成其余的工作。

为什么不创建一个Pet类型的基类呢

public abstract class Pet{

    public abstract void Eat();

}

让这只猫和狗从接口继承
IPet

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

您可以让宠物派生自一个公共基类:

public abstract class Animal
{
    protected Animal() { }

    public abstract void Eat();
}
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
    }
}
然后让Cat和Dog从这个基类派生:

public abstract class Animal
{
    protected Animal() { }

    public abstract void Eat();
}
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();
    }
}
有没有办法使用接口或继承来解决这个问题

接口:制作一个IEat或IPet接口或任何你想表达的概念。使接口具有Eat方法。让Cat和Dog实现此接口。让Pet属性为该类型

继承:制作一个抽象的基类Animal或Pet或任何您想要表示的概念。在基类上创建一个抽象方法。让Cat和Dog从此基类继承。让Pet属性为该类型

这两者有什么区别

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

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

关于接口的问题是,您可以拥有任意数量的接口。但是您只能直接从一个基类继承,所以如果要使用继承,您必须确保正确地继承。继承的问题是,人们最终制造了“车辆”这样的基类,然后他们说“军用车辆是一种车辆”和“舰船是一种车辆”,现在你陷入了困境:驱逐舰的基类是什么?它既是一艘船又是一辆军用车辆,不可能两者兼而有之非常小心地选择“继承轴心”

是否有一种方法可以将宠物设置为Object类型,但仍然可以访问分配给它的任何动物类的属性

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

在C#4中,您可以使用“dynamic”在运行时获得对Pet中对象的Eat方法的动态分派


你不想这样做的原因是,如果有人把水果或手锯放在宠物的财产里,然后试图让它吃,它会崩溃并可怕地死去。编译时检查的目的是减少程序的脆弱性。如果你有办法进行更多的编译时检查,那就用它。

这是作业吗?我觉得很像作业。家庭作业问题没有什么问题,但它们应该被标记为这样。如果是与家庭作业相关的问题,请在问题中添加“家庭作业”标签。我不喜欢这个人。Pet.Eat(),请以错误的方式阅读。我可能担心这个人会对宠物做什么。。那么Person.FeedPet()呢。看在动物的份上……哈哈,这不是家庭作业——我只是用那种方式写的,这样每个人都能理解这个问题。如果我把它们放在这里,我使用的类和属性的实际名称会读起来像中文。也许我应该从事教学工作?;)@原因:不用担心,这可能是关于多态性的教科书作业:)使
Pet
类型为
icant
的属性变得不必要。此外,对于保证成功的强制转换操作,决不能使用
as
运算符,您需要的是显式强制转换<代码>as在取消引用之前应该跟一个空测试,因为可能有失败的强制转换。当您调用
person.Pet.Eat()
时,它如何知道要运行哪个类的Eat函数?这取决于
Pet
属性的实际类型。@Abe Miessler:编译时它不知道。当它知道person.Pet中实际存储的实例类型时,就会在运行时做出决定。因此,通过将抽象类作为person类的成员,您可以将任何派生类分配给该成员?IE
Person.Pet=新猫()
Person.Pet=新狗()?@Abe Miessler,是的,你可以将它分配给任何派生类。如果我养了一把手锯作为宠物,我可能会疯狂到认为它会吃东西。@Jeff:事实上,如果你不知道鹰和手锯的区别,你就有问题了。我的建议是:等待南风。谢谢,这很有帮助,也很机智。一旦我得到足够的支持,我会投票支持的。