Perché la mia class pubblica non può estendere una class interna?

Io davvero non capisco.

Se la class base è astratta e destinata esclusivamente a fornire funzionalità comuni alle sottoclassi pubbliche definite nell’assembly, perché non dovrebbe essere dichiarata interna?

Non voglio che la class astratta sia visibile per codificare all’esterno dell’assembly. Non voglio che il codice esterno lo sappia.

Attingendo da una class, esponi la funzionalità della class base attraverso tuo figlio.

Dal momento che la class figlio ha una visibilità maggiore rispetto a quella dei genitori, esporrà membri che altrimenti verrebbero protetti.

Non è ansible violare il livello di protezione della class genitore implementando un bambino con maggiore visibilità.

Se la class base è pensata per essere utilizzata da classi di bambini pubblici, è necessario rendere pubblico anche il genitore.

L’altra opzione è di mantenere il “genitore” interno, renderlo non astratto e usarlo per comporre le classi figlie e utilizzare un’interfaccia per forzare le classi a implementare la funzionalità:

public interface ISomething { void HelloWorld(); } internal class OldParent : ISomething { public void HelloWorld(){ Console.WriteLine("Hello World!"); } } public class OldChild : ISomething { OldParent _oldParent = new OldParent(); public void HelloWorld() { _oldParent.HelloWorld(); } } 

AGGIORNAMENTO: questa domanda è stata object del mio blog il 13 novembre 2012 . Guardalo per qualche altro pensiero su questo problema. Grazie per la bella domanda!


Hai ragione; non deve essere così. Altre lingue OO consentono “ereditarietà privata”, per cui il fatto che D erediti da B può essere sfruttato solo dal codice che ha la capacità di vedere B.

Questa è stata una decisione progettuale dei designer C # originali. Sfortunatamente sono fuori dalla mia scrivania in questo momento – mi sto prendendo un paio di giorni liberi per il lungo weekend – quindi non ho le note sul design del linguaggio del 1999 di fronte a me. Se ci penso quando torno, li sfoglio e vedo se c’è una giustificazione per questa decisione.

La mia personale opinione è che l’ereditarietà debba essere usata per rappresentare “è un tipo di” relazioni; cioè, l’ereditarietà dovrebbe rappresentare la semantica del dominio modellato nel linguaggio . Cerco di evitare situazioni in cui l’ereditarietà viene utilizzata come meccanismo di condivisione del codice . Come altri hanno già detto, probabilmente è meglio preferire la composizione all’ereditarietà se ciò che si vuole rappresentare è “questa class condivide meccanismi di implementazione con altre classi”.

Penso che la cosa più vicina che puoi fare sia impedire ad altri assembly di creare la class astratta rendendola interna del costruttore, per citare da MSDN :

Un costruttore interno impedisce che la class astratta venga utilizzata come class base di tipi che non si trovano nello stesso assembly della class astratta.

Puoi quindi provare ad aggiungere un editorBrowsableAttribute alla class per provare a nasconderlo da Intellisense (anche se ho avuto risultati misti che lo usano per essere onesto) o mettere la class base in uno spazio dei nomi annidato, come MyLibrary.Internals per MyLibrary.Internals dal resto delle tue lezioni.

Penso che stai mescolando le preoccupazioni qui, e C # è la colpa, in realtà (e Java prima).

L’ereditarietà dovrebbe servire come meccanismo di categorizzazione, mentre è spesso utilizzata per il riutilizzo del codice.

Per il riutilizzo del codice è sempre stato noto che la composizione batte l’ereditarietà. Il problema con C # è che ci offre un modo così semplice di ereditare:

 class MyClass : MyReusedClass { } 

Ma per comporre, dobbiamo farlo da soli:

 class MyClass { MyReusedClass _reused; // need to expose all the methods from MyReusedClass and delegate to _reused } 

Quello che manca è un costrutto come un tratto (pdf) , che porterà la composizione allo stesso livello di usabilità dell’eredità.

C’è una ricerca sui tratti in C # (pdf) , e sarebbe simile a questa:

 class MyClass { uses { MyTrait; } } 

Anche se mi piacerebbe vedere un altro modello (quello dei ruoli di Perl 6).

AGGIORNARE:

Come nota a margine, il linguaggio Oxygene ha una funzione che consente di debind tutti i membri di un’interfaccia a una proprietà membro che implementa tale interfaccia:

 type MyClass = class(IReusable) private property Reused : IReusable := new MyReusedClass(); readonly; implements public IReusable; end; 

Qui, tutti i membri dell’interfaccia di IReusable saranno esposti tramite MyClass e delegheranno tutti alla proprietà Reused . Ci sono alcuni problemi con questo approccio, però.

UN ALTRO AGGIORNAMENTO:

Ho iniziato a implementare questo concetto di composizione automatica in C #: dai un’occhiata a NRoles .

Penso che ciò violerebbe il Principio di sostituzione di Liskov .

In casi come questo, ho usato le classi interne e preferisco la composizione sull’ereditarietà. C’è qualcosa nella tua progettazione che vieta di contenere tutte queste funzionalità nella tua class interna, e quindi le tue classi pubbliche contengono un’istanza di questa class interna?