Ereditarietà multipla in C #


212

Poiché l'ereditarietà multipla è dannosa (rende la fonte più complicata) C # non fornisce direttamente un tale modello. Ma a volte sarebbe utile avere questa capacità.

Ad esempio, sono in grado di implementare il modello di ereditarietà multipla mancante usando interfacce e tre classi simili:

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

Ogni volta che aggiungo un metodo a una delle interfacce, devo cambiare anche la classe FirstAndSecond .

C'è un modo per iniettare più classi esistenti in una nuova classe come è possibile in C ++?

Forse esiste una soluzione che utilizza un qualche tipo di generazione del codice?

Oppure può apparire così (sintassi c # immaginaria):

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

In modo che non sia necessario aggiornare la classe FirstAndSecond quando modifico una delle interfacce.


MODIFICARE

Forse sarebbe meglio considerare un esempio pratico:

Hai una classe esistente (ad esempio un client TCP basato su testo basato su ITextTcpClient) che usi già in diverse posizioni all'interno del tuo progetto. Ora senti la necessità di creare un componente della tua classe per renderlo facilmente accessibile agli sviluppatori di moduli Windows.

Per quanto ne so, attualmente hai due modi per farlo:

  1. Scrivi una nuova classe ereditata dai componenti e implementa l'interfaccia della classe TextTcpClient usando un'istanza della classe stessa come mostrato con FirstAndSecond.

  2. Scrivi una nuova classe che eredita da TextTcpClient e in qualche modo implementa IComponent (non l'ho ancora provato).

In entrambi i casi devi lavorare per metodo e non per classe. Dato che sai che avremo bisogno di tutti i metodi di TextTcpClient e Component, sarebbe la soluzione più semplice combinare questi due in una sola classe.

Per evitare conflitti, ciò può essere fatto mediante la generazione di codice in cui il risultato potrebbe essere modificato in seguito, ma digitarlo a mano è un vero dolore nel culo.


Nella misura in cui questa non è semplicemente un'eredità multipla sotto mentite spoglie, come è meno complicata?
Harpo,

Pensando ai nuovi metodi di estensione in 3.5 e al modo in cui funziona (generazione di chiamate dei membri statici), questa potrebbe essere una delle prossime evoluzioni del linguaggio .NET.
Larry,

A volte mi chiedo perché le persone non si limitino a ... classe A: classe B: classe C?
Chibueze Opata,

@NazarMerza: il link è cambiato. Ora: il problema con l'ereditarietà multipla .
Craig McQueen,

9
Non lasciarti ingannare dalla propaganda. Il tuo esempio dimostra che l'ereditarietà multipla è utile e le interfacce sono solo una soluzione per la mancanza di esso
Kemal Erdogan,

Risposte:


125

Poiché l'ereditarietà multipla è dannosa (rende la fonte più complicata) C # non fornisce direttamente un tale modello. Ma a volte sarebbe utile avere questa capacità.

C # e .net CLR non hanno implementato l'MI perché non hanno ancora concluso come interagirebbe tra C #, VB.net e le altre lingue, non perché "renderebbe la fonte più complessa"

L'MI è un concetto utile, le domande senza risposta sono come: - "Cosa fai quando hai più classi base comuni nelle diverse superclassi?

Perl è l'unica lingua con cui abbia mai lavorato dove MI funziona e funziona bene. .Net potrebbe benissimo introdurlo un giorno, ma non ancora, il CLR supporta già l'MI ma, come ho già detto, non ci sono ancora costrutti linguistici oltre a quello.

Fino ad allora sei bloccato con oggetti proxy e più interfacce :(


39
Il CLR non supporta l'ereditarietà multipla dell'implementazione, solo l'ereditarietà multipla dell'interfaccia (che è supportata anche in C #).
Jordão,

4
@Jordão: Per completezza: è possibile che i compilatori creino MI per i loro tipi nel CLR. Ha i suoi avvertimenti, ad esempio non è conforme a CLS. Per ulteriori informazioni, consultare questo articolo (2004) blogs.msdn.com/b/csharpfaq/archive/2004/03/07/…
dvdvorle

2
@MrHappy: articolo molto interessante. In realtà ho studiato in qualche modo la composizione del tratto per C #, dai un'occhiata.
Jordão

10
@MandeepJanjua Non ho rivendicato nulla del genere, ho detto 'potrebbe anche introdurlo'. Resta il fatto che lo standard ECMA CLR fornisce i macchinari IL per l'ereditarietà multipla, solo che nulla lo utilizza pienamente.
IanNorton,

4
L'ereditarietà multipla FYI non è male e non rende il codice così complicato. Ho pensato di menzionarlo.
Dmitri Nesteruk,

214

Considera solo l'uso della composizione invece di provare a simulare l'ereditarietà multipla. Puoi usare le interfacce per definire quali classi compongono la composizione, ad esempio: ISteerableimplica una proprietà di tipo SteeringWheel, IBrakableimplica una proprietà di tipo BrakePedal, ecc.

Una volta fatto ciò, è possibile utilizzare la funzionalità Metodi di estensione aggiunta a C # 3.0 per semplificare ulteriormente i metodi di chiamata su tali proprietà implicite, ad esempio:

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

13
Questo è il punto però: un'idea come questa faciliterebbe la composizione.
Jon Skeet,

9
Sì, ma ci sono casi d'uso in cui hai davvero bisogno dei metodi come parte dell'oggetto principale
David Pierre,

9
Sfortunatamente i dati variabili dei membri non sono accessibili nei metodi di estensione, quindi è necessario esporli come interni o (ug) pubblici, anche se penso che la composizione per contratto sia il modo migliore per risolvere l'ereditarietà multipla.
cfeduke,

4
Risposta eccellente! Illustrazione concisa, facile da capire, molto utile. Grazie!
AJ.

3
Potremmo voler verificare se myCarha terminato lo sterzo a sinistra prima di chiamare Stop. Potrebbe ribaltarsi se Stopapplicato mentre myCarè a velocità eccessiva. : D
Devraj Gadhavi,

16

Ho creato un post-compilatore C # che abilita questo tipo di cose:

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

È possibile eseguire il post-compilatore come evento post-build di Visual Studio:

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

Nello stesso assieme lo usi in questo modo:

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

In un altro assembly lo usi in questo modo:

var fas = new FirstAndSecond();
fas.FirstMethod();
fas.SecondMethod();

6

Potresti avere una classe base astratta che implementa sia IFirst che ISecond, e quindi ereditare solo da quella base.


Questa è probabilmente la soluzione migliore, ma non necessariamente la migliore idea: p
leppie il

1
non dovresti ancora modificare la classe astratta quando aggiungi metodi alle interfacce?
Rik

Rik: quanto sei pigro, quando devi farlo solo una volta?
leppie,

3
@leppie - "Ogni volta che aggiungo un metodo a una delle interfacce, devo cambiare anche la classe FirstAndSecond." Questa parte della domanda originale non viene risolta da questa soluzione, vero?
Rik

2
Dovresti modificare la classe astratta, ma NON devi modificare altre classi che dipendono da essa. Il dollaro si ferma qui, piuttosto che continuare a cascata per l'intera raccolta di classi.
Joel Coehoorn,

3

MI non è male, tutti quelli che l'hanno (seriamente) lo ADORANO e NON complicano il codice! Almeno non più di altri costrutti potrebbe complicare il codice. Il codice errato è un codice errato indipendentemente dal fatto che MI sia presente nella foto.

Comunque, ho una piccola soluzione per l'ereditarietà multipla che volevo condividere, è a; http://ra-ajax.org/lsp-liskov-substitution-principle-to-be-or-not-to-be.blog oppure puoi seguire il link nel mio sig ... :)


È possibile avere ereditarietà multiple e pur avendo upcasts e downcasts preservare l'identità? Le soluzioni che conosco per i problemi dell'ereditarietà multipla ruotano attorno all'avere cast che non preservano l'identità (se myFooè di tipo Foo, che eredita da Mooe Goo, da cui entrambi ereditano Boo, quindi (Boo)(Moo)myFooe (Boo)(Goo)myFoonon sarebbe equivalente). Sei a conoscenza di approcci per preservare l'identità?
supercat

1
Puoi usare web.archive.org o simili per seguire il link, ma risulta essere una discussione più dettagliata esattamente della soluzione offerta nella domanda originale qui.
MikeBeaton,

2

Nella mia implementazione ho scoperto che l'uso di classi / interfacce per l'MI, sebbene "buona forma", tendeva ad essere un'enorme complicazione in quanto è necessario impostare tutta questa eredità multipla solo per alcune chiamate di funzione necessarie e, nel mio caso, doveva essere fatto letteralmente dozzine di volte in modo ridondante.

Invece era più semplice creare semplicemente "funzioni che chiamano funzioni che chiamano funzioni" in diverse varietà modulari come una sorta di sostituzione OOP. La soluzione a cui stavo lavorando era il "sistema di incantesimi" per un gioco di ruolo in cui gli effetti hanno bisogno di molto mix-and-match funzione di chiamata a dare un'estrema varietà di incantesimi senza codice di ri-scrittura, proprio come l'esempio sembra indicare.

La maggior parte delle funzioni ora può essere statica perché non ho necessariamente bisogno di un'istanza per la logica degli incantesimi, mentre l'eredità delle classi non può nemmeno usare parole chiave virtuali o astratte mentre è statica. Le interfacce non possono usarle affatto.

La codifica sembra molto più veloce e più pulita in questo modo IMO. Se stai semplicemente eseguendo funzioni e non hai bisogno di proprietà ereditate , usa le funzioni.


2

Con C # 8 ora hai praticamente l'ereditarietà multipla tramite l'implementazione predefinita dei membri dell'interfaccia:

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
}

4
Sì, ma nota che, in quanto sopra, non saresti in grado di farlo new ConsoleLogger().Log(someEception)- semplicemente non funzionerà, dovresti esplicitamente trasmettere il tuo oggetto su un ILoggerper usare il metodo di interfaccia predefinito. Quindi la sua utilità è alquanto limitata.
Dmitri Nesteruk,

1

Se riesci a convivere con la restrizione che i metodi di IFirst e ISecond devono interagire solo con il contratto di IFirst e ISecond (come nel tuo esempio) ... puoi fare ciò che chiedi con i metodi di estensione. In pratica, questo è raramente il caso.

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

Quindi l'idea di base è che tu definisca l'implementazione richiesta nelle interfacce ... questa roba richiesta dovrebbe supportare l'implementazione flessibile nei metodi di estensione. Ogni volta che devi "aggiungere metodi all'interfaccia" invece aggiungi un metodo di estensione.


1

Sì, usare Interface è una seccatura perché ogni volta che aggiungiamo un metodo nella classe dobbiamo aggiungere la firma nell'interfaccia. Inoltre, se avessimo già una classe con un sacco di metodi ma nessuna interfaccia per esso? dobbiamo creare manualmente l'interfaccia per tutte le classi da cui vogliamo ereditare. E la cosa peggiore è che dobbiamo implementare tutti i metodi nelle interfacce nella classe child se la classe child deve ereditare dall'interfaccia multipla.

Seguendo il modello di progettazione della facciata possiamo simulare l'ereditarietà da più classi utilizzando gli accessori . Dichiarare le classi come proprietà con {get; set;} all'interno della classe che deve ereditare e tutte le proprietà e i metodi pubblici provengono da quella classe e nel costruttore della classe figlio crea un'istanza delle classi parent.

Per esempio:

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


 }

con questa struttura la classe Child avrà accesso a tutti i metodi e proprietà della classe Father and Mother, simulando l'ereditarietà multipla, ereditando un'istanza delle classi parent. Non è lo stesso, ma è pratico.


2
Non sono d'accordo con il primo paragrafo. Aggiungete solo le firme dei metodi desiderati in OGNI classe all'interfaccia. Ma puoi aggiungere tutti i metodi aggiuntivi a qualsiasi classe che desideri. Inoltre, c'è un clic destro, estrarre l'interfaccia che semplifica il lavoro di estrazione delle interfacce. Infine, il tuo esempio non è in alcun modo ereditario (multiplo o altro), ma è comunque un ottimo esempio di composizione. Purtroppo, avrebbe avuto più valore se avessi usato le interfacce per dimostrare anche DI / IOC usando l'iniezione di proprietà / costruttore. Anche se non voterò verso il basso, non credo sia una buona risposta, scusa.
Francis Rodgers,

1
Ripensando a questa discussione un anno dopo, sono d'accordo che puoi aggiungere tutti i metodi che desideri nella classe senza aggiungere la firma nell'interfaccia, tuttavia ciò renderebbe l'interfaccia incompleta. Inoltre, non sono stato in grado di trovare il tasto destro del mouse - estrarre l'interfaccia nel mio IDE, forse mi mancava qualcosa. Ma la mia più grande preoccupazione è che quando si specifica una firma nell'interfaccia, le classi ereditarie devono implementare quella firma. Penso che questo sia un doppio lavoro e potrebbe portare alla duplicazione dei codici.
Yogi

INTERFACCIA ESTRATTA: fai clic con il pulsante destro del mouse sulla firma della classe, quindi estrai l'interfaccia ... nello stesso processo VS2015 tranne che è necessario fare clic con il pulsante destro del mouse, quindi scegliere Quick Actions and Refactorings...questo è un must, ti farà risparmiare un sacco di tempo
Chef_Code

1

Sembra che tutti stiamo percorrendo il percorso dell'interfaccia con questo, ma l'ovvia altra possibilità, qui, è fare ciò che OOP dovrebbe fare e costruire il tuo albero ereditario ... (non è questo ciò che il design di classe è tutto di?)

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

Questa struttura fornisce blocchi riutilizzabili di codice e, sicuramente, come deve essere scritto il codice OOP?

Se questo particolare approccio non si adatta perfettamente al conto, creiamo semplicemente nuove classi basate sugli oggetti richiesti ...

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

0

L'eredità multipla è una di quelle cose che generalmente causano più problemi di quanti ne risolva. In C ++ si adatta allo schema di darti abbastanza corda per impiccarti, ma Java e C # hanno scelto di seguire la strada più sicura di non darti l'opzione. Il problema più grande è cosa fare se si ereditano più classi che hanno un metodo con la stessa firma che l'ereditario non implementa. Quale metodo di classe dovrebbe scegliere? O non dovrebbe essere compilato? Esiste generalmente un altro modo per implementare la maggior parte delle cose che non si basa sull'ereditarietà multipla.


8
Per favore, non giudicare MI dal C ++, è come giudicare OOP da PHP o automobili da Pintos. Questo problema è facilmente risolvibile: in Eiffel, quando erediti da una classe, devi anche specificare quali metodi vuoi ereditare e puoi rinominarli. Nessuna ambiguità e nessuna sorpresa.
Jörg W Mittag,

2
@mP: no, Eiffel fornisce una vera eredità multipla di implementazione. Rinominare non significa perdere la catena ereditaria, né perderà la castabilità delle classi.
Abel,

0

Se X eredita da Y, ciò ha due effetti in qualche modo ortogonali:

  1. Y fornirà funzionalità predefinite per X, quindi il codice per X deve includere solo elementi diversi da Y.
  2. Quasi ovunque ci si aspetterebbe una Y, al suo posto si può usare una X.

Sebbene l'ereditarietà preveda entrambe le funzionalità, non è difficile immaginare circostanze in cui entrambe potrebbero essere utili senza l'altra. Nessun linguaggio .net che conosco ha un modo diretto di implementare il primo senza il secondo, sebbene si possa ottenere tale funzionalità definendo una classe base che non viene mai utilizzata direttamente e avendo una o più classi che ereditano direttamente da essa senza aggiungere nulla nuovo (tali classi potrebbero condividere tutto il loro codice, ma non sarebbero sostituibili l'una con l'altra). Qualsiasi linguaggio conforme a CLR, tuttavia, consentirà l'uso di interfacce che forniscono la seconda funzionalità di interfacce (sostituibilità) senza il primo (riutilizzo dei membri).


0

lo so lo so anche se non è permesso e così via, a volte ne hai davvero bisogno così per quelli:

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

come nel mio caso volevo fare questa classe b: Form (sì, windows.forms) classe c: b {}

perché metà della funzione era identica e con l'interfaccia è necessario riscriverle tutte


1
Il tuo esempio non descrive l'ereditarietà multipla, quindi quale problema stai cercando di risolvere? Un vero esempio di ereditarietà multipla mostrerebbe class a : b, c(implementando eventuali buchi contrattuali necessari). Forse i tuoi esempi sono appena semplificati?
M.Babcock,

0

Poiché la domanda di ereditarietà multipla (MI) si presenta di volta in volta, vorrei aggiungere un approccio che affronti alcuni problemi con il modello di composizione.

Costruisco sul IFirst, ISecond, First, Second, FirstAndSecondapproccio, come è stato presentato nella domanda. Riduco il codice di esempio a IFirst, poiché il modello rimane lo stesso indipendentemente dal numero di interfacce / classi di base MI.

Supponiamo che con l'MI Firste Secondentrambi deriverebbero dalla stessa classe baseBaseClass , usando solo gli elementi dell'interfaccia pubblicaBaseClass

Questo può essere espresso, aggiungendo un riferimento al contenitore BaseClassnella Firste Secondapplicazione:

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

Le cose diventano più complicate, quando BaseClasssi fa riferimento agli elementi dell'interfaccia protetta o quando Firste Secondsarebbero classi astratte in MI, che richiedono alle loro sottoclassi di implementare alcune parti astratte.

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 # consente alle classi nidificate di accedere ad elementi protetti / privati ​​delle loro classi di contenimento, quindi può essere utilizzata per collegare i bit astratti Firstdall'implementazione.

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

È coinvolto un po 'di plateplate, ma se l'implementazione effettiva di FirstMethod e SecondMethod è sufficientemente complessa e la quantità di metodi privati ​​/ protetti accessibili è moderata, questo modello può aiutare a superare la mancanza di eredità multipla.


0

Questo è in linea con la risposta di Lawrence Wenham, ma a seconda del tuo caso d'uso, potrebbe essere o meno un miglioramento - non hai bisogno dei setter.

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

Ora qualsiasi oggetto che sa come ottenere una persona può implementare IGetPerson e avrà automaticamente i metodi GetAgeViaPerson () e GetNameViaPerson (). Da questo punto in poi tutto il codice Persona va in IGetPerson, non in IPerson, a parte i nuovi ivar, che devono andare in entrambi. E usando tale codice, non devi preoccuparti se il tuo oggetto IGetPerson è in realtà un IPerson.


0

Ciò è ora possibile tramite le partialclassi, ognuna di esse può ereditare una classe da sola, facendo in modo che l'oggetto finale erediti tutte le classi di base. Puoi saperne di più qui .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.