2010-09-07 2 views
5

En C#, j'ai trois classes: Personne, Chat et Chien.Comment autoriser la classe Class être de types multiples/flexibles dans C#?

Les deux classes Cat et Dog ont la méthode Eat().

Je souhaite que la classe Person possède une propriété 'Pet'. Je veux être en mesure d'appeler la méthode Eat du chat et du chien via la personne via quelque chose comme Person.Pet.Eat() mais je ne peux pas parce que la propriété Pet doit être de type Cat ou Chien.

Actuellement je contourne ceci avec deux propriétés dans la classe Person: PetDog et PetCat.

C'est correct pour l'instant, mais si je voulais 100 types d'animaux différents comme animaux de compagnie alors je ne veux pas vraiment avoir 100 propriétés Pet différentes dans la classe Person.

Y a-t-il un moyen de contourner cette utilisation des interfaces ou de l'héritage? Existe-t-il un moyen pour que Pet puisse être de type Object mais accéder aux propriétés de la classe d'animal qui lui est affectée?

+7

Est-ce que ce sont les devoirs? –

+2

Ça sent le travail à moi. Il n'y a rien de mal avec les devoirs, mais ils devraient être signalés comme tels. Ajouter un tag "devoirs" à vos questions si c'est un devoir. –

+2

Je n'aime pas ce Person.Pet.Eat(), lire dans le mauvais sens, je pourrais avoir peur de ce que la personne va faire à l'animal de compagnie .. Que diriez-vous de Person.FeedPet() à la place. Pour l'amour des animaux .. –

Répondre

12

Vous pourriez avoir les animaux proviennent d'une classe de base commune:

public abstract class Animal 
{ 
    protected Animal() { } 

    public abstract void Eat(); 
} 

Et puis avoir Cat et Dog dériver de cette classe de base:

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 
    } 
} 

Et votre classe Person aura une propriété de type Animal:

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

Et quand vous avez une instance de la personne:

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

Vous pouvez également fournir des implémentation commune de la méthode Eat dans la classe de base pour éviter d'avoir à la surcharger dans la classe dérivée es:

public abstract class Animal 
{ 
    protected Animal() { } 

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

Notez que Animal est encore une classe abstraite pour l'empêcher d'être instancié directement.

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(); 
    } 
} 
+0

Comment sait-il quelle fonction Eat de la classe exécuter lorsque vous appelez 'person.Pet.Eat()'? –

+0

Selon le type réel de la propriété 'Pet'. –

+0

@Abe Miessler: Ce n'est pas au moment de la compilation. La décision est prise au moment de l'exécution, quand il sait quel type d'instance est réellement stocké dans 'person.Pet'. – Timwi

1

Utilisation d'une interface;

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

Ensuite, vous pouvez appeler

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

effectuer les null contrôles corrects que vous allez.

+2

Faire la propriété 'Pet' de type' ICanEat' rendra le casting inutile. De plus, vous ne devez * jamais * utiliser l'opérateur 'as 'pour une opération de transtypage réussie, une distribution explicite est ce dont vous avez besoin. 'as' devrait être suivi d'un test nul avant de déréférencer en raison de la possibilité d'un casting raté. – Ani

1

Utilisez une classe abstraite.

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

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

public class Person { 
    public Pet Pet; 
} 

Je suis sûr que vous pouvez faire le reste.

0

Pourquoi ne pas créer une classe de base de type Pet

public abstract class Pet{ 

    public abstract void Eat(); 

} 
0

Demandez ce chat et chien hériter d'une interface IPet

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

Y at-il un moyen de contourner cela en utilisant des interfaces ou héritage?

Oui. Interfaces: créez une interface IEat ou IPet ou tout autre concept que vous voulez représenter. Faites en sorte que l'interface ait une méthode Eat. Avez-Cat and Dog mettre en œuvre cette interface. Avoir la propriété Pet être de ce type. Héritage: Crée une classe de base abstraite Animal ou Animal de compagnie ou tout autre concept que vous voulez représenter. Faire une méthode abstraite Mangez sur la classe de base. Avoir le chat et le chien héritent de cette classe de base. Avoir la propriété Pet être de ce type.

Quelle est la différence entre ces deux-là?

Utilisez des interfaces pour modéliser l'idée "X sait comment faire Y". IDisposable, par exemple, signifie «Je sais comment disposer de la ressource importante que je retiens». Ce n'est pas un fait sur ce que l'objet est, c'est un fait sur ce que fait l'objet .

Utilisez l'héritage pour modéliser l'idée de "X est une sorte de Y". Un chien est une sorte d'animal. En ce qui concerne les interfaces, vous pouvez en avoir autant que vous le souhaitez. Mais vous héritez directement de la classe , donc vous devez vous assurer que vous avez bien compris si vous voulez utiliser l'héritage.Le problème avec l'héritage est que les gens finissent par faire des classes de base comme "Véhicule" et ensuite ils disent "un véhicule militaire est une sorte de véhicule" et "un bateau est une sorte de véhicule" et maintenant vous êtes coincé: quelle est la base? classe de Destroyer? Il est à la fois un navire et un véhicule militaire et il ne peut pas être les deux. Choisissez votre "pivot d'héritage" extrêmement soigneusement.

Y a-t-il un moyen de définir Pet comme étant de type Object mais d'accéder aux propriétés de la classe d'animal qui lui est assignée?

Oui, en C# 4 il y a, mais ne le font pas. Utilisez les interfaces ou l'héritage.

Dans C# 4, vous pouvez utiliser "dynamic" pour obtenir une répartition dynamique au moment de l'exécution de la méthode Eat sur l'objet qui se trouve dans Pet.

La raison pour laquelle vous ne voulez pas faire cela est parce que cela va planter et mourir horriblement si quelqu'un a mis un fruit ou une scie à main dans la propriété Pet et ensuite essayer de le faire manger. Le point de contrôle de la compilation est de réduire la fragilité du programme. Si vous avez un moyen de faire plus de vérifications à la compilation, utilisez-le.

+0

Si j'ai une scie à main comme animal de compagnie, je suis probablement assez fou pour penser qu'il mange. –

+2

@Jeff: En effet, si vous ne connaissez pas un faucon d'une scie à main, vous avez des problèmes. Mon conseil: attendez un vent du sud. –

+0

Merci, c'était très utile et plein d'esprit. Je voterai une fois que j'aurai assez de rep. – Caustix

Questions connexes