2009-08-07 10 views
41

Ho un assembly/progetto comune che ha una classe base astratta, quindi diverse classi derivate che desidero rendere pubbliche per altri assembly.Classe astratta interna: come nascondere l'utilizzo al di fuori del gruppo?

Non voglio la classe di base astratta di presentarsi in altre assemblee a Intellisense, così ho pensato che avrei fatta internal, ma ottengo questo errore:

Inconsistent accessibility: base class 'Settings' is less accessible than class 'IrcSettings' ....

non lo faccio davvero capito. Sono costretto a creare l'abstract classe public e quindi visibile all'esterno di questo assembly.

Come posso rendere questa classe internal invece?

+0

Solo un piccolo punto, è possibile avere interfacce come tipo di base che è interno all'assemblaggio, ma i suoi membri (dell'implementazione) saranno pubblici (disponibili al di fuori dell'assembly). Se la classe astratta non ha alcuna implementazione e i membri sono tutti pubblici, allora si dovrebbe preferire un'interfaccia. – nawfal

risposta

75

Come ho capito, vuoi che la tua classe astratta venga implementata solo da altre classi nello stesso assembly (ad es. è interno) ma le classi derivate potrebbero essere pubbliche.

Il modo per fare questo è quello di rendere la classe base astratta pubblico, ma dare un costruttore di default interna:

public abstract class MyClass 
{ 
    internal MyClass() { } 
} 

Questo consentirà MyClass (e quindi i suoi membri) per essere visibile ed utilizzabile per classi all'esterno dell'assembly, ma le classi esterne all'assembly non possono ereditarlo (verrà visualizzato un errore di compilazione).

Edit: Se le classi che possono essere visto da assembly esterni ereditano da MyClass, non si può impedire MyClass da anche essere visto - per esempio, mostrando in IntelliSense. Tuttavia, è possibile impedire loro di essere utilizzato seguendo quanto sopra.

+3

La mia domanda non riguarda il missue. L'ho già impedito. Si tratta di non inquinare l'Intellisense con una classe che è inutilizzabile al di fuori dell'assemblea. – m3ntat

+0

@ m3ntat vedere la mia risposta modificata. –

+1

You_cannot_ rende le classi base meno accessibili delle classi derivate. Puoi _can_ rendere invece interni i membri della classe base (ma non la classe stessa). – thecoop

9

La classe base astratta deve essere pubblica, poiché l'intera gerarchia ereditaria per una classe deve essere visibile. Ciò garantisce il funzionamento del polimorfismo ed è valido; tuttavia tutti i membri delle classi base possono essere interni (incluso il costruttore) e quindi non utilizzabili all'esterno dell'assieme

+2

Che cosa è il downvote? – thecoop

+1

A meno che non mi sbagli, questo non ha nulla a che fare con il polimorfismo: una classe può implementare un'interfaccia interna e trasformare quell'interfaccia in una classe astratta non cambia nulla in modo che i metodi siano ereditati, ecc. In teoria, si potrebbe perfettamente definire una classe che ha una classe base interna e usarla in altri assembly. È solo che C# (o .NET?) Non lo consente. –

6

Non c'è davvero molto vantaggio per quello che stai cercando di ottenere, ma quello che stai cercando di ottenere è simile a questo.

Avere la classe base astratta in 1 assemblaggio con tutto interno. Nel AssemblyInfo per il montaggio che è necessario aggiungere

[assembly:InternalsVisibleTo("cs_friend_assemblies_2")] 

Poi in un altro assembly si hanno tutte le classi che si desidera a disposizione del pubblico. Nota che sarai ancora in grado di accedere alla classe base da intellisense per qualsiasi codice all'interno di cs_friend_assemblies_2 o qualunque cosa tu chiami il tuo assembly ma non altrove.

+1

penso che questo sia l'unico suggerimento per l'OP di REALIZZARE REALMENTE quello che vuole, anche se questo è un metodo abbastanza orribile, ma ancora +1 – Letseatlunch

+1

Non trovo valore in ciò che l'OP vuole ottenere ma non direi che questo è un approccio orribile. Questo in pratica apre "amici assemblati" in .NET. Le assemblee di amici usate in situazioni sbagliate possono portare a codice terribile e quadri terribili, ma ciò potrebbe derivare da cattive pratiche. Trovo che l'uso più necessario degli assembly di amici sia per i progetti di test e talvolta nelle dipendenze di ServiceLayer (ex UpdateBy è {public get; set interno protetto} e al Servizio è consentito assegnare UpdatedBy ma non altrove). –

+1

mi sono imbattuto in questa domanda perché avevo un'esigenza simile all'OP e ho scoperto che il tuo era l'unico che avrebbe funzionato per l'OP. Nel mio caso, tuttavia, non funzionerebbe per determinati motivi e questa non era un'opzione. Quindi non direi che questa sia una soluzione "orribile", che è un'esagerazione, ma ha fatto sentire che questo è più simile a un lavoro approssimativo che non funzionerà in tutti i casi. – Letseatlunch

4

Non è possibile rendere simultaneamente la classe disponibile per altri assiemi per ereditarietà ma anche privata, in modo che non possa essere visibile ad altri utenti. È possibile rendere la classe interna e esporla a un assembly specifico (se si tratta di un'assembly di amici) utilizzando l'attributo [InternalsVisibleTo], ma non penso che questo sia ciò che si desidera.

Se si desidera mantenere il codice (diverso da classi derivate) di essere in grado di istanziare la vostra classe di base, si potrebbe dare un costruttore protetto:

abstract class MyBaseClass 
{ 
    protected MyBaseClass() { ... } // only inheritors can access this... 
} 

È possibile nascondere i membri della classe da Intellisense utilizzando il EditorBrowsable attributo:

abstract class MyBaseClass 
{ 
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] 
    public void SomeMethodToBeHidden() { } 
} 

va notato che alcune persone hanno segnalato problemi con l'IDE non sempre rispettando questo attributo.

+1

EditorBrowsableAttribute è valido solo per i membri della classe, non per le dichiarazioni di classe. –

+0

Questo è quello che sto cercando, ma non sembra rispettarlo in Intellisense che ho appena provato. – m3ntat

+0

Inoltre, la marcatura dell'estratto di classe impedisce che venga creata un'istanza. Avere un costruttore predefinito protetto no. –

0

Gli altri assembly erediteranno mai dalla classe base astratta o da una qualsiasi delle classi pubbliche che ereditano dalla classe base astratta?

In tal caso, è necessario rendere pubblica la classe base astratta. È sufficiente creare metodi che non si desidera visualizzare all'esterno dell'assieme interno.

In caso contrario, forse le interfacce possono essere d'aiuto? Definisci le interfacce pubbliche, fai in modo che le tue classi pubbliche le implementino e fornisci una fabbrica per ottenere istanze. In questo modo l'unica cosa che intellisense vede al di fuori del gruppo è l'interfaccia.

Questo aiuto?

1

Per quanto mi riguarda, questo non è un problema. Osservare:

public abstract class Foo { 
    public void virtual Bar() { 
     // default implementation 
    } 
} 

public class NormalFoo : Foo { } 

public class SpecialFoo : Foo { 
    public override void Bar() { 
     // special implementation 
    } 
} 

var foolist = new List<Foo>(); 

foolist.Add(new NormalFoo()); 
foolist.Add(new SpecialFoo()); 

foreach (var f in foolist) { 
    f.Bar(); 
} 

È possibile che questo non avrebbe funzionato a tutti, senza polimorfismo - essere in grado di fare riferimento alle istanze di diverse classi derivate attraverso la loro interfaccia comune, la classe base astratta. Quello che vuoi fare è toglierlo e paralizzare l'usabilità della tua gerarchia di classi. Non penso che dovresti continuare su questa strada.

0

Un modo per ovviare a questa limitazione è utilizzare la composizione anziché l'ereditarietà (ci sono othergood reasons anche per questo). Per esempio, invece di:

internal abstract class MyBase 
{ 
    public virtual void F() {} 
    public void G() {} 
} 

public class MyClass : MyBase // error; inconsistent accessibility 
{ 
    public override void F() { base.F(); /* ... */ } 
} 

fare questo:

public interface IMyBase 
{ 
    void F(); 
} 

internal sealed class MyBase2 : IMyBase 
{ 
    public void F() {} 
    public void G() {} 
} 

public sealed class MyClass2 : IMyBase 
{ 
    private readonly MyBase2 _decorated = new MyBase2(); 
    public void F() { _decorated.F(); /* ... */ } 
    public void G() { _decorated.G(); } 
} 

è possibile omettere l'interfaccia IMyBase del tutto se il pubblico non ha bisogno di sapere su di esso e le vostre parti interne non lo fanno neanche.