2010-10-10 13 views
3

Supposons que nous avons déclaré que ces deux classes:Pourquoi est-il légal de stocker un type dérivé dans une référence de classe de base?

public class Animal 
{ 
    //..... 
} 

public class Dog : Animal 
{ 
    //..... 
} 

Eh bien, ma question est: pourquoi ci-dessous ligne de code est valide?

Animal animal = new Dog();

EDIT:

Dans e-book, "Professional C# 2008", il y a un paragraphe qui dit:

Il est toujours plus sûr de stocker un type dérivé dans les une référence de classe de base.

Répondre

5

Vous créez un nouveau Dog, mais vous le traitez en tant que Animal.

Ceci est particulièrement utile lorsque vous souhaitez avoir un comportement qui est détenu ou exposé par le Animal, mais le Dog peut alors remplacer celui avec quelque chose de différent. Le Dog peut être utilisé comme Dog ou comme Animal, parce que c'est les deux.

Edit: ici est un exemple rapide, et je vais utiliser une classe de base abstraite afin que vous puissiez voir la valeur réelle de cet arrangement:

public abstract class Animal 
{ 
    public abstract void Move(); 

    public virtual void MakeSignatureSound() 
    { 
     Console.WriteLine("Ugggg"); 
    } 
} 

public class Dog : Animal 
{ 
    public override void Move() 
    { 
     RunLikeAPuppy(); 
    } 

    public override void MakeSignatureSound() 
    { 
     Console.WriteLine("Woof"); 
    } 
} 

public class CaveMan : Animal 
{ 
    public override void Move() 
    { 
     RunLikeANeanderthal(); 
    } 
} 

public class Cat : Animal 
{ 
    public override void Move() 
    { 
     RunLikeAKitteh(); 
    } 

    public override void MakeSignatureSound() 
    { 
     Console.WriteLine("Meioww"); 
    } 
} 

avis deux choses:

  • trois dérivés de la classe Animalavaient pour passer outre la fonction Move(), parce que dans ma classe de base i pris la décision que tous les animaux doivent avoir un Move(), mais je n'ai pas précisé comment ils doivent se déplacer - qui est à l'animal de préciser
  • la classe CaveMan ne l'emportait pas sur la fonction MakeSignatureSound(), car il avait déjà été défini dans la classe de base et était suffisante pour lui

maintenant, si je fais ceci:

Animal caveman = new CaveMan(); 
Animal dog = new Dog(); 
caveman.MakeSignatureSound(); 
dog.MakeSignatureSound(); 

je vais obtenir ceci sur la console:

Ugggg 
Woof 

Mais parce que je l'ai utilisé une classe de base abstraite, je ne peux pas instancier une instance directement, je ne peux pas le faire:

Animal animal = new Animal(); 

En tant que développeur, je veux faire en sorte que lorsque les autres (ou moi-même) créer un nouveau Animal, il doit être un type spécifique, pas seulement un Animal pur qui n'avait aucun comportement ou caractéristiques.

+0

Beau commentaire. Je veux dire la méthode prioritaire en sous-classe. Pourriez-vous s'il vous plaît écrire un exemple simple à ce sujet ici? – odiseh

+0

Un grand merci à vous :) – odiseh

9

La raison pour laquelle cela fonctionne est qu'un chien is an animaux (chien parce que inherits de l'animal) donc il est légal d'assigner un objet chien à une variable d'animaux de type.

Affecter dans l'autre sens n'est pas légal. La ligne suivante donnera une erreur, car les animaux ne hérite pas de chien:

// Error: Cannot implicitly convert type 'Animal' to 'Dog'. 
// An explicit conversion exists (are you missing a cast?) 
Dog dog = new Animal(); 

Dans certains cas, vous pouvez avoir une variable d'animaux de type mais vous savez qu'en réalité il doit être un chien. Dans ce cas, vous pouvez utiliser un transtypage pour effectuer l'assignation, mais cette distribution peut échouer au moment de l'exécution si l'objet n'était pas réellement un chien.

Animal animal = new Cat(); 

// Unable to cast object of type 'Cat' to type 'Dog'. 
Dog dog = (Dog)animal; 
+1

Merci pour votre commentaire. J'ai édité mon paragraphe :) – odiseh

1

Parce que Dog hérite de Animal - donc Dog peuvent être traités comme si elle était un Animal (il est un Animal en fait, il prend en charge la même « interface » - qui est, toutes les propriétés/méthodes sur Animal sont garantis mis en œuvre par Dog).

1

Il est à cause de la relation que vous avez déclaré ici

public class Dog : Animal 

Qu'est-ce que cette ligne est essentiellement à dire que chaque chien est un animal

Maintenant, si chaque chien est un animal, ce que cela signifie, c'est que je prends un chien mais je me réfère à lui comme un animal. Un exemple analogue serait de se référer à vous comme un mammifère - vous êtes humain mais êtes également un mammifère, donc si je vous appelais un mammifère qui serait toujours correct.

Il ne vous qualifie pas nécessairement en détail mais reste correct en soi.

+0

Comme vous l'avez dit: "... se référant à lui comme un animal" Cela signifie-t-il que nous faisons un upcast implicite? – odiseh

+0

faisant référence à une classe de base ne signifie pas upcasting? – odiseh

+0

@odiseh - Mon mauvais !! Tu as raison!! Le terme technique est "upcasting" .. je me suis trompé !! – InSane

1

En C# (et d'autres langues) chaque objet dispose de deux types: type statique et de type dynamique. Le type statique d'un objet (dans votre exemple: Animal) est déterminé au moment de la compilation et le type dynamique (dans votre exemple: Dog) est déterminé au moment de l'exécution. Considérez ce qui suit:

Animal animal; 
if (userInput) 
    animal = new Wolf(); 
else 
    animal = new Dog(); 

Le compilateur n'a aucun moyen de déterminer quel animal de type dynamique aura.Il est uniquement déterminé au moment de l'exécution
Le type dynamique doit toujours être au moins le type statique d'un objet. Celles-ci ne sont pas autorisées et entraîneront des erreurs de compilation:

Dog d = new Animal(); // X 
Animal a = new Car(); // X (assuming car does not inherit from animal) 

Pourquoi est-ce utile? Contrairement aux langages dynamiquement typés (où il n'y a que du type dynamique et pas de statique), le compilateur peut vérifier les erreurs de frappe. C'est bien parce que nous voulons attraper les erreurs le plus tôt possible.
Dans mon exemple, le compilateur ne sait pas si animal est Wolf ou Dog, mais les deux dérivent de Animal il peut donc être sûr que toute opération Aminal peut être effectuée sur animal. Essayer d'effectuer d'autres opérations entraînera une erreur de compilation. D'autre part, nous pouvons réaliser des choses puissantes qui ne sont pas possibles sans le système «dual type». Supposons Aminal a une opération eat. Les deux Dog et Wolf mettent également en œuvre manger, chacun à sa manière. Regardons l'exemple suivant:

Animal a = new Animal(); 
Animal d = new Dog(); 

a.eat(); // Animal's eat 
d.eat(); // Dog's eat 

Ici, nous pouvons voir que, bien que le type statique de d est Animal, la version actuelle de la fonction appelée est déterminée par type dynamique. Cela nous permet de faire une chose appelée polymorphisme. Je vais vous montrer un exemple:

Animal zoo[100]; // each animal in the zoo array is a static type Animal 
zoo[0] = new Dog(); // first element of the array is of dynamic type Dog 
zoo[1] = new Cat(); 
zoo[2] = new Rabbit(); 
... 
// Now the array holds different types of animals. We want to feed them all, but each one in it's own way. 
foreach(Animal a in zoo) 
    a.eat(); 

Pour résumer:

  • type statique connu au moment de la compilation. Utilisé pour déterminer si une opération est légale à effectuer sur un objet.
  • Type dynamique connu uniquement lors de l'exécution. Permet de choisir quelle opération effectuer. Il y a un autre terme pour cela: expédition dynamique
2

Juste pour ajouter un exemple supplémentaire quand cela serait utile ...

Vous pourriez avoir une liste fortement typé des animaux ...

List<Animal> animals = new List<Animal>(); 

et vous ajouteriez des instances d'animaux dans cette collection.

animals.Add(new Dog()); 
animals.Add(new Cat()); 

Si vous avez des animaux comme ci-dessous.

class Animal 
{ 
    public abstract string Run(); 
} 

class Dog : Animal 
{ 
    public override string Run() 
    { 
     return "Running into a wall."; 
    } 

} 

class Cat : Animal 
{ 
    public override string Run() 
    { 
    return "Running up a tree"; 
    } 

} 

vous pouvez alors boucler en toute sécurité à travers la collection.

foreach(var animal in animals) 
    Console.WriteLine(animal.Run()); 

Cela introduit d'autres concepts comme des méthodes abstraites et primordial que vous devriez examiner aussi ..

Espérons que cela est d'usage.

Bonne chance!

0

simplement supposer cette ligne n'est pas légal Animal animal = new Dog();

public class Animal 
{ 
    //..... 
} 


public class Dog : Animal 
{ 
    //..... 
} 

Maintenant, dans mon programme principal j'ai une fonction appelée alimentation.

void Feed(Dog d) 
{ 

    //.... 
} 

Et puis je l'ai ajouté un autre chat de classe

public class Cat : Animal 
{ 
    //..... 
} 

puis dans mon programme principal que je vais devoir écrire une autre fonction pour alimentation spécialement pour la classe de chat

void Feed(Dog d) 
{ 

    //.... 
} 

void Feed(Cat d) 
{ 

    //.... 
} 

Pour évitez de coder des fonctions en double (dans le cas de classes dérivées), il est légal et sûr de stocker un type dérivé dans une référence de classe de base.

Maintenant, au lieu de deux fonctions ci-dessus, je peux écrire une seule fonction d'avance et prend un paramètre d'une classe de base.

Animal animal = new Dog(); 

Animal animal2 = new Cat(); 

Feed(animal); 
Feed(animal2); 

void Feed(Animal A) 
{ 

    //.... 
}