Как разрешить свойство класса быть нескольких / гибких типов в C #? - PullRequest
5 голосов
/ 07 сентября 2010

В C # у меня есть три класса: Person, Cat и Dog.

Оба класса Cat и Dog имеют метод Eat ().

Я хочу, чтобы у класса Person было свойство Pet.

Я хочу иметь возможность вызывать метод Eat для Cat и Dog через Person через что-то вроде Person.Pet.Eat (), но я не могу, потому что свойство Pet должно быть любого типаКошка или Собака.

В настоящее время я обхожу это с двумя свойствами в классе Person: PetDog и PetCat.

Пока все нормально, но если бы я хотел иметь в качестве домашних животных 100 различных видов животных, я бы не хотел иметь 100 различных свойств домашних животных в классе Person.

Есть лиспособ обойти это с помощью интерфейсов или наследования?Есть ли способ, которым я могу присвоить Pet тип Object, но при этом получить доступ к свойствам любого класса животных, назначенного ему?

Ответы [ 6 ]

12 голосов
/ 07 сентября 2010

Вы можете получить домашних животных из общего базового класса:

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();
    }
}
11 голосов
/ 07 сентября 2010

Есть ли способ обойти это, используя интерфейсы или наследование?

Да.

Интерфейсы: создайте интерфейс IEat или IPet или любую другую концепцию, которую вы хотите представить.Сделайте так, чтобы в интерфейсе был метод Eat.Пусть Кошка и Собака реализуют этот интерфейс.Имейте свойство Pet этого типа.

Наследование: Создайте абстрактный базовый класс Animal или Pet или любую другую концепцию, которую вы хотите представить.Сделай абстрактный метод Eat на базовом классе.Пусть Кошка и Собака унаследуют от этого базового класса.Имейте свойство Pet такого типа.

В чем разница между этими двумя?

Используйте интерфейсы для моделирования идеи "X знает, как сделать Y".Например, IDisposable означает «Я знаю, как распорядиться важным ресурсом, который я держу».Это не факт о том, что объект является , это факт о том, что объект делает .

Использование наследования для моделирования идеи "X являетсявроде Y ".Собака - это разновидность животного.

Особенность интерфейсов в том, что вы можете иметь их столько, сколько захотите.Но вы можете наследовать только напрямую от одного базового класса, поэтому вы должны убедиться, что вы правильно поняли, если собираетесь использовать наследование.Проблема с наследованием заключается в том, что люди заканчивают тем, что создают базовые классы, такие как «Транспортное средство», а затем они говорят: «Военный автомобиль - это вид транспортного средства» и «Корабль - это вид транспортного средства», и теперь вы застряли: что является основойкласс Разрушителя?Это и корабль, и военный автомобиль, и не может быть одновременно . Очень тщательно выбирайте свою «опору наследования» .

Есть ли способ, которым я могу присвоить питомцу тип Object, но при этом получить доступ к свойствам того класса, который ему присвоен??

Да, в C # 4 есть, но не делают .Используйте интерфейсы или наследование.

В C # 4 вы можете использовать «динамический» для получения динамической отправки во время выполнения методу Eat для объекта, который находится в Pet.

Причина, по которой вы не хотите этого делать, заключается в том, что это может привести к краху и ужасной смерти, если кто-то поместит Фрукт или Ножовку в свойство питомца , а затем попытается заставить его съесть.Целью проверок во время компиляции является уменьшение хрупкости программ.Если у вас есть способ сделать больше проверок во время компиляции, используйте его.

1 голос
/ 07 сентября 2010

Используйте абстрактный класс.

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

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

public class Person {
    public Pet Pet;
}

Я уверен, что вы можете сделать все остальное.

1 голос
/ 07 сентября 2010

Использование интерфейса;

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();

выполняя правильные нулевые проверки на ходу.

0 голосов
/ 07 сентября 2010

Имейте эту кошку и собаку наследовать от интерфейса IPet

public interface IPet
{
    bool hungry();
    void Eat();
}
0 голосов
/ 07 сентября 2010

Почему бы не создать базовый класс типа Pet

public abstract class Pet{

    public abstract void Eat();

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...