Question

Je vais avoir quelques problèmes de succession que j'ai un groupe de l'inter-reliés les classes abstraites qui doivent tous être substituée ensemble pour créer une mise en œuvre du client.Idéalement, je voudrais faire quelque chose comme ce qui suit:

abstract class Animal
{
  public Leg GetLeg() {...}
}

abstract class Leg { }

class Dog : Animal
{
  public override DogLeg Leg() {...}
}

class DogLeg : Leg { }

Cela permettrait à quiconque, à l'aide de la classe Chien pour obtenir automatiquement des Coudes et de toute personne utilisant l'Animal de la classe à avoir les Jambes.Le problème est que le substituée fonction doit être du même type que la classe de base afin de ne compiler.Je ne vois pas pourquoi il ne devrait pas, mais, depuis DogLeg est implicitement moulage à la Jambe.Je sais qu'il ya beaucoup de façons de contourner cela, mais je suis plus curieux de savoir pourquoi ce n'est pas possible/implémenté en C#.

MODIFIER:J'ai modifié un peu, depuis que je suis en fait à l'aide des propriétés, au lieu de fonctions dans mon code.

MODIFIER:Je l'ai changé de retour à des fonctions, parce que la réponse ne s'applique à cette situation (covariance sur le paramètre de la valeur d'une propriété de l'ensemble de la fonction ne devrait pas de travail).Désolé pour les fluctuations de!Je me rends compte qu'il fait un grand nombre de réponses semble pas être pertinent.

Était-ce utile?

La solution

La réponse courte est que GetLeg est invariante dans son type de retour.La réponse peut être trouvée ici: La Covariance et la contravariance

Je tiens à ajouter que, bien que l'héritage est généralement la première abstraction de l'outil que la plupart des développeurs de sortir de leur boîte à outils, il est presque toujours possible d'utiliser la composition à la place.La Composition est un peu plus de travail pour l'API de développeur, mais rend l'API plus utile pour ses consommateurs.

Autres conseils

Clairement, vous aurez besoin d'un jet si vous êtes d'exploitation sur un Coude cassé.

Le chien doit retourner une Jambe n'est pas un DogLeg comme type de retour.La classe réelle peut être un DogLeg, mais le point est de découpler l'utilisateur Chien n'a pas à savoir sur les Coudes, ils ont seulement besoin de savoir sur les Jambes.

Changement:

class Dog : Animal
{
  public override DogLeg GetLeg() {...}
}

pour:

class Dog : Animal
{
  public override Leg GetLeg() {...}
}

Ne pas faire ceci:

 if(a instanceof Dog){
       DogLeg dl = (DogLeg)a.GetLeg();

il défait le but de la programmation du type abstrait.

La raison de le cacher DogLeg est parce que le GetLeg fonction dans la classe abstraite renvoie un Résumé de la Jambe.Si vous remplacez les GetLeg vous devez retourner une Jambe.C'est le point de disposer d'une méthode dans une classe abstraite.Pour propager que la méthode de childern.Si vous voulez que les utilisateurs du Chien à savoir sur les Coudes faire une méthode appelée GetDogLeg et retourner un DogLeg.

Si vous pouviez faire comme la question le demandeur le souhaite, ensuite, tous les utilisateurs de l'Animal aurait besoin de savoir à propos de TOUS les animaux.

Il est parfaitement valide désir d'avoir la signature d'une méthode de remplacement ont un type de retour est un sous-type du type de retour de la méthode de remplacement (ouf).Après tout, ils sont de type à l'exécution compatible.

Mais le C# n'a pas encore de support "covariante des types de retour" dans les méthodes de remplacement (à la différence de C++ [1998] & Java [2004]).

Vous aurez besoin de travailler autour et faire faire dans un avenir prévisible, comme Eric Lippert a déclaré dans son blog [Juin 19, 2008]:

Ce genre d'écart est appelé "type de retour de la covariance".

nous n'avons pas de plans pour mettre en œuvre ce type de variance en C#.

abstract class Animal
{
  public virtual Leg GetLeg ()
}

abstract class Leg { }

class Dog : Animal
{
  public override Leg GetLeg () { return new DogLeg(); }
}

class DogLeg : Leg { void Hump(); }

Le faire comme cela, alors vous pouvez tirer profit de l'abstraction dans votre client:

Leg myleg = myDog.GetLeg();

Alors si vous en avez besoin, vous pouvez la lancer:

if (myleg is DogLeg) { ((DogLeg)myLeg).Hump()); }

Totalement artificiel, mais le point est de sorte que vous pouvez le faire:

foreach (Animal a in animals)
{
   a.GetLeg().SomeMethodThatIsOnAllLegs();
}

Tout en conservant la possibilité d'avoir un spécial Bosse de méthode sur les Coudes.

Vous pouvez utiliser des génériques et des interfaces à mettre en œuvre qu'en C#:

abstract class Leg { }

interface IAnimal { Leg GetLeg(); }

abstract class Animal<TLeg> : IAnimal where TLeg : Leg
 { public abstract TLeg GetLeg();
   Leg IAnimal.GetLeg() { return this.GetLeg(); }
 }

class Dog : Animal<Dog.DogLeg>
 { public class DogLeg : Leg { }
   public override DogLeg GetLeg() { return new DogLeg();}
 } 

GetLeg() doit retourner la Jambe pour être un remplacement.Votre Chien classe cependant, il est encore de retour Coudé objets car ils sont les enfants de la classe de la Jambe.les clients peuvent ensuite coulé et les manipuler comme des coudes.

public class ClientObj{
    public void doStuff(){
    Animal a=getAnimal();
    if(a is Dog){
       DogLeg dl = (DogLeg)a.GetLeg();
    }
  }
}

Le concept qui est à l'origine des problèmes est décrit à http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)

Non pas qu'il est beaucoup utilisé, mais il est peut-être intéressant de noter la Java prend en charge covariante les retours, et donc, ce serait exactement la façon dont vous espériez.À l'exception évidemment que Java ne possède pas de propriétés ;)

Peut-être qu'il est plus facile de voir le problème avec un exemple:

Animal dog = new Dog();
dog.SetLeg(new CatLeg());

Voilà qui devrait compiler si vous êtes Chien compilé, mais nous avons certainement ne voulez pas un tel mutant.

La question devrait Chien[] être un Animal[], ou IList<Dog> IList<Animal>?

C# a des implémentations d'interface pour répondre à cette question:

abstract class Leg { }
class DogLeg : Leg { }

interface IAnimal
{
    Leg GetLeg();
}

class Dog : IAnimal
{
    public override DogLeg GetLeg() { /* */ }

    Leg IAnimal.GetLeg() { return GetLeg(); }
}

Si vous avez un Chien à travers une référence de type de Chien, puis l'appel de GetLeg() retournera un DogLeg.Si vous avez le même objet, mais la référence est de type IAnimal, puis il sera de retour une Jambe.

Bon, je comprends que je peux les jeter, mais cela signifie que le client doit savoir que les Chiens ont des Coudes.Ce que je me demande si il y a des raisons techniques, ce n'est pas possible, étant donné qu'une conversion implicite existe.

@Brian Leahy Évidemment, si vous êtes d'exploitation uniquement sur elle comme d'une Jambe, il n'y a pas de nécessité ou la raison pour jeter le sort.Mais si il y a un DogLeg un Chien ou un comportement spécifique, il y a parfois des raisons que la distribution est nécessaire.

Vous pouvez également revenir à l'interface ILeg que les deux Jambes et/ou Coudé mettre en œuvre.

La chose importante à retenir est que vous pouvez utiliser un type dérivé chaque endroit où vous utilisez le type de base (vous pouvez passer le Chien de n'importe quelle méthode/propriété/domaine/variable qui attend Animal)

Nous allons profiter de cette fonction:

public void AddLeg(Animal a)
{
   a.Leg = new Leg();
}

Un parfaitement de fonction valide, maintenant, nous allons appeler la fonction comme ça:

AddLeg(new Dog());

Si la propriété de Chien.La jambe n'est pas de type Jambe la AddLeg fonction soudain contient une erreur et ne peut pas être compilé.

@Luc

Je pense que votre peut-être l'incompréhension de l'héritage.Chien.GetLeg() retournera un DogLeg objet.

public class Dog{
    public Leg GetLeg(){
         DogLeg dl = new DogLeg(super.GetLeg());
         //set dogleg specific properties
    }
}


    Animal a = getDog();
    Leg l = a.GetLeg();
    l.kick();

la méthode appelée sera Chien.GetLeg();et Coudé.Coup de pied() (je suis en supposant une méthode de la Jambe.coup de pied() existe) il y a des pour, la déclaration de type de retour d'être Coudé est inutile, parce que c'est ce rendu, même si le type de retour pour le Chien.GetLeg() est la Jambe.

Vous pouvez obtenir ce que vous désirez à l'aide d'un générique, avec une contrainte, comme suit:

abstract class Animal<LegType> where LegType : Leg
{
    public abstract LegType GetLeg();
}

abstract class Leg { }

class Dog : Animal<DogLeg>
{
    public override DogLeg GetLeg()
    {
        return new DogLeg();
    }
}

class DogLeg : Leg { }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top