Héritage multiple en C #

212

Étant donné que l'héritage multiple est mauvais (cela rend la source plus compliquée), C # ne fournit pas directement un tel modèle. Mais parfois, il serait utile d'avoir cette capacité.

Par exemple, je suis capable d'implémenter le modèle d'héritage multiple manquant en utilisant des interfaces et trois classes comme ça:

public interface IFirst { void FirstMethod(); }
public interface ISecond { void SecondMethod(); }

public class First:IFirst 
{ 
    public void FirstMethod() { Console.WriteLine("First"); } 
}

public class Second:ISecond 
{ 
    public void SecondMethod() { Console.WriteLine("Second"); } 
}

public class FirstAndSecond: IFirst, ISecond
{
    First first = new First();
    Second second = new Second();
    public void FirstMethod() { first.FirstMethod(); }
    public void SecondMethod() { second.SecondMethod(); }
}

Chaque fois que j'ajoute une méthode à l'une des interfaces, je dois également changer la classe FirstAndSecond .

Existe-t-il un moyen d'injecter plusieurs classes existantes dans une nouvelle classe comme c'est possible en C ++?

Peut-être existe-t-il une solution utilisant une sorte de génération de code?

Ou cela peut ressembler à ceci (syntaxe c # imaginaire):

public class FirstAndSecond: IFirst from First, ISecond from Second
{ }

Pour qu'il n'y ait pas besoin de mettre à jour la classe FirstAndSecond lorsque je modifie l'une des interfaces.


ÉDITER

Il serait peut-être préférable de considérer un exemple pratique:

Vous avez une classe existante (par exemple, un client TCP basé sur du texte basé sur ITextTcpClient) que vous utilisez déjà à différents endroits dans votre projet. Vous ressentez maintenant le besoin de créer un composant de votre classe pour être facilement accessible aux développeurs de formulaires Windows.

Autant que je sache, vous avez actuellement deux façons de procéder:

  1. Écrivez une nouvelle classe héritée des composants et implémente l'interface de la classe TextTcpClient à l'aide d'une instance de la classe elle-même, comme illustré avec FirstAndSecond.

  2. Écrivez une nouvelle classe qui hérite de TextTcpClient et implémente en quelque sorte IComponent (ne l'ont pas encore essayé).

Dans les deux cas, vous devez travailler par méthode et non par classe. Puisque vous savez que nous aurons besoin de toutes les méthodes de TextTcpClient et Component, ce serait la solution la plus simple pour simplement combiner ces deux en une seule classe.

Pour éviter les conflits, cela peut être fait par génération de code où le résultat pourrait être modifié par la suite, mais le taper à la main est une pure douleur dans le cul.

Martin
la source
Dans la mesure où il ne s'agit pas simplement d'un héritage multiple déguisé, comment est-ce moins compliqué?
harpo
En pensant aux nouvelles méthodes d'extension de la 3.5 et à son fonctionnement (génération d'appels de membres statiques), cela pourrait être l'une des prochaines évolutions du langage .NET.
Larry
Parfois, je me demande pourquoi les gens ne font pas que ... classe A: classe B: classe C?
Chibueze Opata
@NazarMerza: le lien a changé. Maintenant: le problème de l'héritage multiple .
Craig McQueen
9
Ne laissez pas la propagande vous tromper. Votre exemple montre que l'héritage multiple est utile et que les interfaces ne sont qu'une solution de contournement pour son absence
Kemal Erdogan

Réponses:

125

Étant donné que l'héritage multiple est mauvais (cela rend la source plus compliquée), C # ne fournit pas directement un tel modèle. Mais parfois, il serait utile d'avoir cette capacité.

C # et le .net CLR n'ont pas implémenté MI car ils n'ont pas encore conclu comment il interagirait entre C #, VB.net et les autres langages, pas parce que "cela rendrait la source plus complexe"

L'IM est un concept utile, les questions sans réponse sont celles comme: - "Que faites-vous lorsque vous avez plusieurs classes de base communes dans les différentes superclasses?

Perl est la seule langue avec laquelle j'ai jamais travaillé où MI fonctionne et fonctionne bien. .Net pourrait bien l'introduire un jour mais pas encore, le CLR supporte déjà MI mais comme je l'ai dit, il n'y a pas encore de construction de langage pour cela.

Jusque-là, vous êtes coincé avec des objets proxy et plusieurs interfaces à la place :(

IanNorton
la source
39
Le CLR ne prend pas en charge l'héritage d'implémentations multiples, uniquement l'héritage d'interfaces multiples (qui est également pris en charge en C #).
Jordão
4
@ Jordão: Par souci d'exhaustivité: il est possible pour les compilateurs de créer des MI pour leurs types dans le CLR. Il a ses mises en garde, il n'est pas conforme CLS par exemple. Pour plus d'informations, consultez cet article (2004) blogs.msdn.com/b/csharpfaq/archive/2004/03/07/…
dvdvorle
2
@MrHappy: article très intéressant. J'ai en fait étudié un moyen de composition de traits pour C #, jetez un œil.
Jordão
10
@MandeepJanjua Je n'ai rien prétendu de tel, j'ai dit 'pourrait bien l'introduire' Le fait demeure que la norme ECMA CLR fournit le mécanisme IL pour l'héritage multiple, juste que rien ne l'utilise pleinement.
IanNorton
4
L'héritage multiple FYI n'est pas mauvais et ne rend pas le code si compliqué. Je pensais juste le mentionner.
Dmitri Nesteruk
214

Envisagez simplement d'utiliser la composition au lieu d'essayer de simuler l'héritage multiple. Vous pouvez utiliser des interfaces pour définir quelles classes composent la composition, par exemple: ISteerableimplique une propriété de type SteeringWheel, IBrakableimplique une propriété de type BrakePedal, etc.

Une fois que vous avez fait cela, vous pouvez utiliser la fonctionnalité Méthodes d'extension ajoutée à C # 3.0 pour simplifier davantage les méthodes d'appel sur ces propriétés implicites, par exemple:

public interface ISteerable { SteeringWheel wheel { get; set; } }

public interface IBrakable { BrakePedal brake { get; set; } }

public class Vehicle : ISteerable, IBrakable
{
    public SteeringWheel wheel { get; set; }

    public BrakePedal brake { get; set; }

    public Vehicle() { wheel = new SteeringWheel(); brake = new BrakePedal(); }
}

public static class SteeringExtensions
{
    public static void SteerLeft(this ISteerable vehicle)
    {
        vehicle.wheel.SteerLeft();
    }
}

public static class BrakeExtensions
{
    public static void Stop(this IBrakable vehicle)
    {
        vehicle.brake.ApplyUntilStop();
    }
}


public class Main
{
    Vehicle myCar = new Vehicle();

    public void main()
    {
        myCar.SteerLeft();
        myCar.Stop();
    }
}
Chris Wenham
la source
13
C'est le point cependant - une idée comme celle-ci faciliterait la composition.
Jon Skeet
9
Oui, mais il existe des cas d'utilisation où vous avez vraiment besoin des méthodes dans le cadre de l'objet principal
David Pierre
9
Malheureusement, les données des variables membres ne sont pas accessibles dans les méthodes d'extension, vous devez donc les exposer en tant que public interne ou (ug), bien que je pense que la composition par contrat est le meilleur moyen de résoudre l'héritage multiple.
cfeduke
4
Excellente réponse! Illustration concise, facile à comprendre et très utile. Je vous remercie!
AJ.
3
Nous pourrions vouloir vérifier s'il myCara fini de tourner à gauche avant d'appeler Stop. Il peut se renverser s'il est Stopappliqué myCarà une vitesse excessive. : D
Devraj Gadhavi
16

J'ai créé un post-compilateur C # qui permet ce genre de chose:

using NRoles;

public interface IFirst { void FirstMethod(); }
public interface ISecond { void SecondMethod(); }

public class RFirst : IFirst, Role {
  public void FirstMethod() { Console.WriteLine("First"); }
}

public class RSecond : ISecond, Role {
  public void SecondMethod() { Console.WriteLine("Second"); }
}

public class FirstAndSecond : Does<RFirst>, Does<RSecond> { }

Vous pouvez exécuter le post-compilateur en tant qu'événement post-build de Visual Studio:

C: \ some_path \ nroles-v0.1.0-bin \ nutate.exe "$ (TargetPath)"

Dans le même assemblage, vous l'utilisez comme ceci:

var fas = new FirstAndSecond();
fas.As<RFirst>().FirstMethod();
fas.As<RSecond>().SecondMethod();

Dans un autre assemblage, vous l'utilisez comme ceci:

var fas = new FirstAndSecond();
fas.FirstMethod();
fas.SecondMethod();
Jordão
la source
6

Vous pouvez avoir une classe de base abstraite qui implémente à la fois IFirst et ISecond, puis hériter uniquement de cette base.

Joel Coehoorn
la source
C'est probablement la meilleure solution, mais pas nécessairement la meilleure idée: p
leppie
1
n'auriez-vous pas encore à modifier la classe abstraite lorsque vous ajoutez des méthodes aux interafces?
Rik
Rik: à quel point êtes-vous paresseux, quand vous n'avez à faire cela qu'une seule fois?
leppie
3
@leppie - "Chaque fois que j'ajoute une méthode à l'une des interfaces, je dois également changer la classe FirstAndSecond." Cette partie de la question d'origine n'est pas abordée par cette solution, n'est-ce pas?
Rik
2
Vous devrez éditer la classe abstraite, mais vous n'aurez PAS à éditer d'autres classes qui en dépendent. Le mâle s'arrête là, plutôt que de continuer à se répercuter sur toute la collection de classes.
Joel Coehoorn
3

L'IM n'est PAS mauvais, tout le monde qui l'a (sérieusement) utilisé l'aime et cela ne complique PAS le code! Au moins pas plus que d'autres constructions peuvent compliquer le code. Un mauvais code est un mauvais code, que MI soit ou non dans l'image.

Quoi qu'il en soit, j'ai une jolie petite solution pour l'héritage multiple que je voulais partager, c'est à; http://ra-ajax.org/lsp-liskov-substitution-principle-to-be-or-not-to-be.blog ou vous pouvez suivre le lien dans mon sig ... :)

Thomas Hansen
la source
Est-il possible d'avoir un héritage multiple et tout en ayant des upcasts et downcasts de préserver l'identité? Les solutions que je connais pour les problèmes d'héritage multiple tournent autour d'avoir des transtypages qui ne préservent pas l'identité (si myFooest de type Foo, qui hérite de Mooet Goo, qui héritent de Boo, alors (Boo)(Moo)myFooet (Boo)(Goo)myFoone seraient pas équivalents). Connaissez-vous des approches préservant l'identité?
supercat
1
Vous pouvez utiliser web.archive.org ou similaire pour suivre le lien, mais il s'avère que c'est une discussion plus détaillée de la solution proposée dans la question d'origine ici.
MikeBeaton
2

Dans ma propre implémentation, j'ai trouvé que l'utilisation de classes / interfaces pour MI, bien que "bonne forme", avait tendance à être une complication excessive, car vous devez configurer tout cet héritage multiple pour seulement quelques appels de fonction nécessaires, et dans mon cas, devait être fait littéralement des dizaines de fois de manière redondante.

Au lieu de cela, il était plus facile de simplement créer des "fonctions qui appellent des fonctions qui appellent des fonctions" statiques dans différentes variétés modulaires comme une sorte de remplacement de POO. La solution sur laquelle je travaillais était le "système de sorts" pour un RPG où les effets doivent fortement mélanger et assortir la fonction d'appel pour donner une variété extrême de sorts sans réécrire le code, un peu comme l'exemple semble l'indiquer.

La plupart des fonctions peuvent désormais être statiques car je n'ai pas nécessairement besoin d'une instance pour la logique des sorts, alors que l'héritage de classe ne peut même pas utiliser de mots clés virtuels ou abstraits en statique. Les interfaces ne peuvent pas du tout les utiliser.

Le codage semble beaucoup plus rapide et plus propre de cette façon OMI. Si vous ne faites que des fonctions et n'avez pas besoin de propriétés héritées , utilisez des fonctions.

hydrix
la source
2

Avec C # 8, vous avez maintenant pratiquement l'héritage multiple via l'implémentation par défaut des membres de l'interface:

interface ILogger
{
    void Log(LogLevel level, string message);
    void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); // New overload
}

class ConsoleLogger : ILogger
{
    public void Log(LogLevel level, string message) { ... }
    // Log(Exception) gets default implementation
}
Ludmil Tinkov
la source
4
Oui, mais notez que, dans ce qui précède, vous ne seriez pas en mesure de le faire new ConsoleLogger().Log(someEception)- cela ne fonctionnera tout simplement pas, vous devrez explicitement convertir votre objet en an ILoggerpour utiliser la méthode d'interface par défaut. Son utilité est donc quelque peu limitée.
Dmitri Nesteruk
1

Si vous pouvez vivre avec la restriction que les méthodes de IFirst et ISecond ne doivent interagir qu'avec le contrat de IFirst et ISecond (comme dans votre exemple) ... vous pouvez faire ce que vous demandez avec des méthodes d'extension. En pratique, c'est rarement le cas.

public interface IFirst {}
public interface ISecond {}

public class FirstAndSecond : IFirst, ISecond
{
}

public static MultipleInheritenceExtensions
{
  public static void First(this IFirst theFirst)
  {
    Console.WriteLine("First");
  }

  public static void Second(this ISecond theSecond)
  {
    Console.WriteLine("Second");
  }
}

///

public void Test()
{
  FirstAndSecond fas = new FirstAndSecond();
  fas.First();
  fas.Second();
}

Donc, l'idée de base est que vous définissez l'implémentation requise dans les interfaces ... ce truc requis devrait prendre en charge l'implémentation flexible dans les méthodes d'extension. Chaque fois que vous devez "ajouter des méthodes à l'interface", vous ajoutez plutôt une méthode d'extension.

Amy B
la source
1

Oui, utiliser Interface est un problème car chaque fois que nous ajoutons une méthode dans la classe, nous devons ajouter la signature dans l'interface. Et si nous avons déjà une classe avec un tas de méthodes mais pas d'interface? nous devons créer manuellement une interface pour toutes les classes dont nous voulons hériter. Et le pire, c'est que nous devons implémenter toutes les méthodes dans les interfaces de la classe enfant si la classe enfant doit hériter de l'interface multiple.

En suivant le modèle de conception de façade, nous pouvons simuler l'héritage de plusieurs classes à l'aide d' accesseurs . Déclarez les classes en tant que propriétés avec {get; set;} à l'intérieur de la classe qui doit hériter et toutes les propriétés et méthodes publiques proviennent de cette classe, et dans le constructeur de la classe enfant instanciez les classes parentes.

Par exemple:

 namespace OOP
 {
     class Program
     {
         static void Main(string[] args)
         {
             Child somechild = new Child();
             somechild.DoHomeWork();
             somechild.CheckingAround();
             Console.ReadLine();
         }
     }

     public class Father 
     {
         public Father() { }
         public void Work()
         {
             Console.WriteLine("working...");
         }
         public void Moonlight()
         {
             Console.WriteLine("moonlighting...");
         }
     }


     public class Mother 
     {
         public Mother() { }
         public void Cook()
         {
             Console.WriteLine("cooking...");
         }
         public void Clean()
         {
             Console.WriteLine("cleaning...");
         }
     }


     public class Child 
     {
         public Father MyFather { get; set; }
         public Mother MyMother { get; set; }

         public Child()
         {
             MyFather = new Father();
             MyMother = new Mother();
         }

         public void GoToSchool()
         {
             Console.WriteLine("go to school...");
         }
         public void DoHomeWork()
         {
             Console.WriteLine("doing homework...");
         }
         public void CheckingAround()
         {
             MyFather.Work();
             MyMother.Cook();
         }
     }


 }

avec cette structure, la classe Child aura accès à toutes les méthodes et propriétés de la classe Father and Mother, simulant l'héritage multiple, héritant d'une instance des classes parentes. Pas tout à fait pareil mais c'est pratique.

Yogi
la source
2
Je ne suis pas d'accord avec le premier paragraphe. Vous ajoutez uniquement les signatures des méthodes souhaitées dans CHAQUE classe à l'interface. Mais vous pouvez ajouter autant de méthodes supplémentaires à n'importe quelle classe que vous le souhaitez. De plus, il existe un clic droit, une interface d'extraction qui simplifie l'extraction des interfaces. Enfin, votre exemple n'est en aucun cas un héritage (multiple ou autre), c'est cependant un excellent exemple de composition. Hélas, cela aurait plus de mérite si vous aviez utilisé des interfaces pour démontrer également DI / IOC en utilisant l'injection de constructeur / propriété. Bien que je ne vote pas, je ne pense pas que ce soit une bonne réponse, désolé.
Francis Rodgers
1
En regardant ce fil un an plus tard, je conviens que vous pouvez ajouter autant de méthodes que vous le souhaitez dans la classe sans ajouter de signature dans l'interface, cependant, cela rendrait l'interface incomplète. De plus, je n'ai pas pu trouver l'interface de clic droit - extraire dans mon IDE, peut-être que je manquais quelque chose. Mais, ma plus grande préoccupation est, lorsque vous spécifiez une signature dans l'interface, les classes héritées doivent implémenter cette signature. Je pense que c'est un double travail et pourrait conduire à une duplication des codes.
Yogi
EXTRAIRE L'INTERFACE: faites un clic droit sur la signature de la classe, puis extrayez l'interface ... dans VS2015 même processus sauf que vous devez faire un clic droit, puis choisissez Quick Actions and Refactorings...c'est un must, cela vous fera gagner beaucoup de temps
Chef_Code
1

Nous semblons tous suivre le chemin de l'interface avec cela, mais l'autre possibilité évidente, ici, est de faire ce que la POO est censée faire, et de construire votre arbre d'héritage ... (n'est-ce pas ce que la conception de classe est tout à propos?)

class Program
{
    static void Main(string[] args)
    {
        human me = new human();
        me.legs = 2;
        me.lfType = "Human";
        me.name = "Paul";
        Console.WriteLine(me.name);
    }
}

public abstract class lifeform
{
    public string lfType { get; set; }
}

public abstract class mammal : lifeform 
{
    public int legs { get; set; }
}

public class human : mammal
{
    public string name { get; set; }
}

Cette structure fournit des blocs de code réutilisables et, comment doit-on écrire le code POO?

Si cette approche particulière ne correspond pas tout à fait au projet de loi, nous créons simplement de nouvelles classes en fonction des objets requis ...

class Program
{
    static void Main(string[] args)
    {
        fish shark = new fish();
        shark.size = "large";
        shark.lfType = "Fish";
        shark.name = "Jaws";
        Console.WriteLine(shark.name);
        human me = new human();
        me.legs = 2;
        me.lfType = "Human";
        me.name = "Paul";
        Console.WriteLine(me.name);
    }
}

public abstract class lifeform
{
    public string lfType { get; set; }
}

public abstract class mammal : lifeform 
{
    public int legs { get; set; }
}

public class human : mammal
{
    public string name { get; set; }
}

public class aquatic : lifeform
{
    public string size { get; set; }
}

public class fish : aquatic
{
    public string name { get; set; }
}
Paul
la source
0

L'héritage multiple est l'une de ces choses qui pose généralement plus de problèmes qu'il n'en résout. En C ++, cela correspond au modèle de vous donner suffisamment de corde pour vous accrocher, mais Java et C # ont choisi de suivre la voie la plus sûre de ne pas vous donner l'option. Le plus gros problème est de savoir quoi faire si vous héritez de plusieurs classes qui ont une méthode avec la même signature que l'héritier n'implémente pas. Quelle méthode de classe doit-elle choisir? Ou cela ne devrait-il pas être compilé? Il existe généralement une autre façon d'implémenter la plupart des choses qui ne repose pas sur l'héritage multiple.

tloach
la source
8
Veuillez ne pas juger MI par C ++, c'est comme juger OOP par PHP ou les automobiles par Pintos. Ce problème est facilement résolu: dans Eiffel, lorsque vous héritez d'une classe, vous devez également spécifier les méthodes que vous souhaitez hériter et vous pouvez les renommer. Aucune ambiguïté là-bas et aucune surprise non plus.
Jörg W Mittag
2
@mP: non, Eiffel fournit un véritable héritage d'implémentations multiples. Renommer ne signifie pas perdre la chaîne d'héritage, ni perdre la castabilité des classes.
Abel
0

Si X hérite de Y, cela a deux effets quelque peu orthogonaux:

  1. Y fournira la fonctionnalité par défaut pour X, donc le code pour X ne doit inclure que des éléments différents de Y.
  2. Presque partout où un Y serait attendu, un X peut être utilisé à la place.

Bien que l'héritage prévoie les deux fonctionnalités, il n'est pas difficile d'imaginer des circonstances où l'une ou l'autre pourrait être utile sans l'autre. Aucun langage .net que je connaisse n'a un moyen direct d'implémenter le premier sans le second, bien que l'on puisse obtenir une telle fonctionnalité en définissant une classe de base qui n'est jamais utilisée directement, et en ayant une ou plusieurs classes qui en héritent directement sans rien ajouter new (ces classes pourraient partager tout leur code, mais ne seraient pas substituables les unes aux autres). Tout langage compatible CLR, cependant, permettra l'utilisation d'interfaces qui fournissent la deuxième fonctionnalité des interfaces (substituabilité) sans la première (réutilisation des membres).

supercat
la source
0

Je sais que je sais même si ce n'est pas autorisé et ainsi de suite, parfois vous en avez vraiment besoin pour ceux:

class a {}
class b : a {}
class c : b {}

comme dans mon cas, je voulais faire cette classe b: Form (yep les windows.forms) classe c: b {}

car la moitié de la fonction était identique et avec l'interface, vous devez tous les réécrire

ariel
la source
1
Votre exemple ne représente pas l'héritage multiple, alors quel problème essayez- vous de résoudre? Un véritable exemple d'héritage multiple apparaîtrait class a : b, c(implémentant tous les trous de contrat nécessaires). Peut-être que vos exemples sont un peu plus simplifiés?
M.Babcock
0

Étant donné que la question de l'héritage multiple (MI) apparaît de temps en temps, j'aimerais ajouter une approche qui résout certains problèmes avec le modèle de composition.

Je construis sur la IFirst, ISecond, First, Second, FirstAndSecondapproche, comme il a été présenté dans la question. Je réduit l'exemple de code à IFirst, car le modèle reste le même quel que soit le nombre d'interfaces / classes de base MI.

Supposons qu'avec MI Firstet Seconddérivent tous les deux de la même classe de base BaseClass, en utilisant uniquement les éléments d'interface publique deBaseClass

Cela peut être exprimé en ajoutant une référence de conteneur à BaseClassdans l' implémentation Firstet Second:

class First : IFirst {
  private BaseClass ContainerInstance;
  First(BaseClass container) { ContainerInstance = container; }
  public void FirstMethod() { Console.WriteLine("First"); ContainerInstance.DoStuff(); } 
}
...

Les choses deviennent plus compliquées, lorsque les éléments d'interface protégés de BaseClasssont référencés ou quand Firstet Secondseraient des classes abstraites dans MI, nécessitant leurs sous-classes pour implémenter certaines parties abstraites.

class BaseClass {
  protected void DoStuff();
}

abstract class First : IFirst {
  public void FirstMethod() { DoStuff(); DoSubClassStuff(); }
  protected abstract void DoStuff(); // base class reference in MI
  protected abstract void DoSubClassStuff(); // sub class responsibility
}

C # permet aux classes imbriquées d'accéder aux éléments protégés / privés de leurs classes contenantes, donc cela peut être utilisé pour lier les bits abstraits de l' Firstimplémentation.

class FirstAndSecond : BaseClass, IFirst, ISecond {
  // link interface
  private class PartFirst : First {
    private FirstAndSecond ContainerInstance;
    public PartFirst(FirstAndSecond container) {
      ContainerInstance = container;
    }
    // forwarded references to emulate access as it would be with MI
    protected override void DoStuff() { ContainerInstance.DoStuff(); }
    protected override void DoSubClassStuff() { ContainerInstance.DoSubClassStuff(); }
  }
  private IFirst partFirstInstance; // composition object
  public FirstMethod() { partFirstInstance.FirstMethod(); } // forwarded implementation
  public FirstAndSecond() {
    partFirstInstance = new PartFirst(this); // composition in constructor
  }
  // same stuff for Second
  //...
  // implementation of DoSubClassStuff
  private void DoSubClassStuff() { Console.WriteLine("Private method accessed"); }
}

Il y a pas mal de passe-partout impliqués, mais si la mise en œuvre réelle de FirstMethod et SecondMethod est suffisamment complexe et que la quantité de méthodes privées / protégées accessibles est modérée, alors ce modèle peut aider à surmonter le manque d'héritage multiple.

grek40
la source
0

Cela va dans le sens de la réponse de Lawrence Wenham, mais selon votre cas d'utilisation, cela peut ou non être une amélioration - vous n'avez pas besoin des poseurs.

public interface IPerson {
  int GetAge();
  string GetName();
}

public interface IGetPerson {
  IPerson GetPerson();
}

public static class IGetPersonAdditions {
  public static int GetAgeViaPerson(this IGetPerson getPerson) { // I prefer to have the "ViaPerson" in the name in case the object has another Age property.
    IPerson person = getPerson.GetPersion();
    return person.GetAge();
  }
  public static string GetNameViaPerson(this IGetPerson getPerson) {
    return getPerson.GetPerson().GetName();
  }
}

public class Person: IPerson, IGetPerson {
  private int Age {get;set;}
  private string Name {get;set;}
  public IPerson GetPerson() {
    return this;
  }
  public int GetAge() {  return Age; }
  public string GetName() { return Name; }
}

Désormais, tout objet qui sait comment obtenir une personne peut implémenter IGetPerson et il disposera automatiquement des méthodes GetAgeViaPerson () et GetNameViaPerson (). À partir de ce moment-là, tout le code de la personne va dans IGetPerson, pas dans IPerson, à l'exception des nouveaux ivars, qui doivent aller dans les deux. Et en utilisant un tel code, vous n'avez pas à vous soucier de savoir si votre objet IGetPerson est lui-même réellement un IPerson.

William Jockusch
la source
0

C'est désormais possible grâce aux partialclasses, chacune d'elles peut hériter d'une classe à elle seule, faisant que l'objet final hérite de toutes les classes de base. Vous pouvez en savoir plus ici .

Nekuś
la source