C# - pubblicamente i metodi ereditati essere nascosti (ad es.reso privato per classe derivata)

StackOverflow https://stackoverflow.com/questions/106383

  •  01-07-2019
  •  | 
  •  

Domanda

Supponiamo che io sono BaseClass con metodi pubblici A e B, e creare DerivedClass attraverso l'ereditarietà.

ad es.

public DerivedClass : BaseClass {}

Ora voglio sviluppare un metodo C in DerivedClass che utilizzi A e B.C'è un modo posso sostituire i metodi A e B per essere privato in DerivedClass in modo che solo il metodo C è esposto a qualcuno che vuole usare il mio DerivedClass?

È stato utile?

Soluzione

Non è possibile, perché?

In C #, ti viene imposto che se erediti i metodi pubblici, devi renderli pubblici. Altrimenti si aspettano che tu non derivi dalla classe in primo luogo.

Invece di usare la relazione is-a, dovresti usare la relazione has-a.

I progettisti del linguaggio non lo consentono espressamente in modo da poter utilizzare l'ereditarietà in modo più corretto.

Ad esempio, si potrebbe confondere accidentalmente un'auto di classe per derivare da un motore di classe per ottenere la sua funzionalità. Ma un motore è la funzionalità utilizzata dall'auto. Quindi vorresti usare la relazione has-a. L'utente dell'auto non desidera avere accesso all'interfaccia del motore. E l'auto stessa non deve confondere i metodi del motore con i propri. Né future derivazioni di Car.

Quindi non gli consentono di proteggerti da gerarchie ereditarie errate.

Che cosa dovresti fare invece?

Invece dovresti implementare interfacce. Questo ti lascia libero di avere funzionalità usando la relazione has-a.

Altre lingue:

In C ++ devi semplicemente specificare un modificatore prima della classe base di privato, pubblico o protetto. Questo rende tutti i membri della base che erano pubblici a quel livello di accesso specificato. Mi sembra sciocco che non puoi fare lo stesso in C #.

Il codice ristrutturato:

interface I
{
    void C();
}

class BaseClass
{
    public void A() { MessageBox.Show("A"); }
    public void B() { MessageBox.Show("B"); }
}

class Derived : I
{
    public void C()
    {
        b.A();
        b.B();
    }

    private BaseClass b;
}

Comprendo che i nomi delle classi precedenti sono un po 'controversi :)

Altri suggerimenti:

Altri hanno suggerito di rendere pubblici A () e B () e generare eccezioni. Ma questo non ha una classe amichevole da usare per le persone e non ha davvero senso.

Altri suggerimenti

Quando, ad esempio, si tenta di ereditare da un List<object> e si desidera nascondere il membro Add(object _ob) diretto:

// the only way to hide
[Obsolete("This is not supported in this class.", true)]
public new void Add(object _ob)
{
    throw NotImplementedException("Don't use!!");
}

Non è davvero la soluzione più preferibile, ma fa il lavoro. Intellisense accetta ancora, ma in fase di compilazione viene visualizzato un errore:

  

errore CS0619: 'TestConsole.TestClass.Add (TestConsole.TestObject)' è obsoleto: 'Questo non è supportato in questa classe.'

Sembra una cattiva idea. Liskov non sarebbe rimasto colpito.

Se non si desidera che i consumatori di DerivedClass possano accedere ai metodi DeriveClass.A () e DerivedClass.B (), suggerirei che DerivedClass dovrebbe implementare un'interfaccia pubblica IWhateverMethodCIsAbout e che i consumatori di DerivedClass dovrebbero effettivamente parlare IWhateverMethodCIsInformazioni e informazioni sull'implementazione di BaseClass o DerivedClass.

Di che cosa avete bisogno è la composizione non è l'ereditarietà.

class Plane
{
  public Fly() { .. }
  public string GetPilot() {...}
}

Ora, se avete bisogno di un particolare tipo di Aereo, come uno che ha PairOfWings = 2 ma per il resto fa tutto quello che un aereo in grado di..Si eredita piano.Con la presente si dichiara che il vostro derivazione incontra il contratto della classe base, e può essere sostituito senza battere ciglio dove una classe base non è previsto.ad es.LogFlight(Piano) continueranno a lavorare con un Biplano istanza.

Tuttavia, se avete solo bisogno di Volare comportamento per un nuovo Uccello che si desidera creare, e non sono disposti a sostenere il completo di base di classe del contratto, si compone invece.In questo caso, il refactoring del comportamento di metodi per il riutilizzo in un nuovo tipo di Volo.Ora creare e mantenere riferimenti a questa classe sia in Piano e di Uccelli.Non si ereditano, perché l'Uccello non supporta il completo della classe base contratto...( ad es.non può fornire GetPilot() ).

Per lo stesso motivo, non è possibile ridurre la visibilità dei metodi della classe base quando si esegue l'override.. è possibile ignorare e fare una base metodo privato pubblico nella derivazione, ma non viceversa.ad es.In questo esempio, se io derivare un tipo di Piano "BadPlane" e poi sovrascrivere e "Nascondere" GetPilot() - rendere privata;un metodo per cliente LogFlight(Piano p) per la maggior parte dei Piani, ma farà saltare in aria per "BadPlane" se l'attuazione di LogFlight accade per necessità/call GetPilot(). Dal momento che tutte le derivazioni di una classe di base dovrebbero essere sostituibili ovunque una classe base param è previsto, deve essere consentita.

L'unico modo per farlo è quello di utilizzare una relazione Has-A e implementare solo le funzioni che si desidera esporre.

@Brian R. Bondy mi ha indicato un interessante articolo su Nascondersi per eredità e la nuova parola chiave.

http://msdn.microsoft.com/ it-it / library / aa691135 (VS.71) aspx

Quindi, come soluzione alternativa, suggerirei:

class BaseClass
{
    public void A()
    {
        Console.WriteLine("BaseClass.A");
    }

    public void B()
    {
        Console.WriteLine("BaseClass.B");
    }
}

class DerivedClass : BaseClass
{
    new public void A()
    {
        throw new NotSupportedException();
    }

    new public void B()
    {
        throw new NotSupportedException();
    }

    public void C()
    {
        base.A();
        base.B();
    }
}

In questo modo un codice come questo genererà un NotSupportedException :

    DerivedClass d = new DerivedClass();
    d.A();

Nascondersi è una pendenza piuttosto scivolosa. I problemi principali, IMO, sono:

  • Dipende dal tempo di progettazione tipo di dichiarazione dell'istanza, significa che se fai qualcosa del genere BaseClass obj = new SubClass (), quindi chiama obj.A (), nascondere è nascosto. BaseClass.A () verrà eseguito.

  • Nascondersi può oscurare molto facilmente comportamento (o cambiamenti di comportamento) in il tipo di base. Questo è ovviamente meno preoccupazione quando possiedi entrambi lati dell'equazione o se la chiamata "base.xxx" fa parte del tuo membro secondario.

  • Se in realtà possiedi possedere entrambi i lati dell'equazione base / sottoclasse, allora dovresti essere in grado di escogitare una soluzione più gestibile rispetto a nascondere / oscurare istituzionalizzati.

Direi che se hai una base di codice con cui vuoi farlo, non è la base di codice meglio progettata. In genere è un segno di una classe in un livello dell'erarchia che necessita di una certa firma pubblica mentre un'altra classe derivata da quella classe non ne ha bisogno.

Un paradigma di codifica imminente si chiama " Composition over Inheritance. " Ciò gioca direttamente sui principi dello sviluppo orientato agli oggetti (in particolare il principio di responsabilità singola e il principio aperto / chiuso).

Sfortunatamente, il modo in cui a molti sviluppatori è stato insegnato l'orientamento agli oggetti, abbiamo preso l'abitudine di pensare immediatamente all'eredità anziché alla composizione. Tendiamo ad avere classi più grandi che hanno molte responsabilità diverse semplicemente perché potrebbero essere contenute con lo stesso & Quot; Real World & Quot; oggetto. Questo può portare a gerarchie di classi che hanno una profondità di 5+ livelli.

Uno sfortunato effetto collaterale a cui gli sviluppatori normalmente non pensano quando hanno a che fare con l'ereditarietà è che l'ereditarietà costituisce una delle forme più forti di dipendenze che tu possa mai introdurre nel tuo codice. La tua classe derivata ora dipende fortemente dalla classe da cui è stata ereditata. Questo può rendere il tuo codice più fragile a lungo termine e portare a problemi di confusione in cui la modifica di un determinato comportamento in una classe base interrompe le classi derivate in modi oscuri.

Un modo per rompere il codice è attraverso interfacce come menzionato in un'altra risposta. Questa è una cosa intelligente da fare comunque perché vuoi che le dipendenze esterne di una classe si leghino alle astrazioni, non a tipi concreti / derivati. Ciò consente di modificare l'implementazione senza modificare l'interfaccia, il tutto senza effettuare una riga di codice nella classe dipendente.

Preferirei piuttosto che mantenere un sistema con centinaia / migliaia / persino più classi che sono tutte piccole e vagamente accoppiate, piuttosto che avere a che fare con un sistema che fa un uso pesante del polimorfismo / ereditarietà e ha meno classi che sono più strettamente accoppiate .

Forse la migliore risorsa sullo sviluppo orientato agli oggetti è il libro di Robert C. Martin, Sviluppo software agile, principi, schemi e pratiche .

Se sono definiti pubblici in classe originale, non è possibile eseguire l'override di essere privato nella classe derivata.Tuttavia, è possibile rendere pubblico il metodo genera un'eccezione e implementare la propria funzione privata.

Edit:Jorge Ferreira è corretto.

Mentre la risposta alla domanda è " no " ;, c'è un suggerimento che desidero sottolineare per gli altri che arrivano qui (dato che l'OP stava alludendo all'accesso in assemblea da parte di terzi ). Quando altri fanno riferimento a un assembly, Visual Studio dovrebbe onorare il seguente attributo in modo che non venga mostrato in intellisense (nascosto, ma può ANCORA essere chiamato, quindi attenzione):

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]

Se non avessi altra scelta, dovresti essere in grado di utilizzare new su un metodo che nasconde un metodo di tipo base, restituire => throw new NotSupportedException(); e combinarlo con l'attributo sopra.

Un altro trucco dipende dal NON ereditare da una classe base, se possibile, in cui la base ha un'interfaccia corrispondente (come IList<T> per List<T>). Implementazione di interfacce & Quot; esplicitamente & Quot; nasconderà anche questi metodi da intellisense sul tipo di classe. Ad esempio:

public class GoodForNothing: IDisposable
{
    void IDisposable.Dispose() { ... }
}

Nel caso di var obj = new GoodForNothing(), il metodo Dispose() non sarà disponibile su obj. Tuttavia, sarà disponibile a chiunque scriva esplicitamente da IDisposable a <=>.

Inoltre, puoi anche racchiudere un tipo di base anziché ereditarne, quindi nascondere alcuni metodi:

public class MyList<T> : IList<T>
{
    List<T> _Items = new List<T>();
    public T this[int index] => _Items[index];
    public int Count => _Items.Count;
    public void Add(T item) => _Items.Add(item);
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
    void ICollection<T>.Clear() => throw new InvalidOperationException("No you may not!"); // (hidden)
    /*...etc...*/
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top