2009-08-07 11 views
41

Ich habe eine gemeinsame Assembly/Projekt, die eine abstrakte Basisklasse hat, dann mehrere abgeleitete Klassen, die ich für andere Assemblys veröffentlichen möchte.Interne abstrakte Klasse: Wie kann man die Verwendung außerhalb der Baugruppe ausblenden?

Ich will nicht die abstrakte Basisklasse in anderen Baugruppen in Intellisense zeigen, so dass ich dachte, dass ich es internal machen würde, aber ich bekomme diese Fehlermeldung:

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

ich nicht tun hol das wirklich. Ich bin gezwungen, die abstrakte Settings Klasse public, und damit sichtbar außerhalb dieser Baugruppe.

Wie kann ich diese Klasse internal stattdessen machen?

+0

Nur ein kleiner Punkt, Sie können Schnittstellen als Basistyp haben, der innerhalb der Assembly ist, aber seine Mitglieder (der Implementierung) werden öffentlich sein (außerhalb der Assembly verfügbar). Wenn die abstrakte Klasse keine Implementierung hat und alle Mitglieder öffentlich sind, sollte man die Schnittstelle bevorzugen. – nawfal

Antwort

75

Ich verstehe, dass Ihre abstrakte Klasse nur von anderen Klassen in derselben Baugruppe implementiert werden soll (zB ist intern), aber die abgeleiteten Klassen könnten öffentlich sein.

Die Art und Weise, dies zu tun, ist die abstrakte Basisklasse öffentlich zu machen, aber es einen internen Standardkonstruktors geben:

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

Dies ermöglicht MyClass (und seine Mitglieder daher) sichtbar und nutzbar zu Klassen sein außerhalb Ihrer Assembly, aber Klassen außerhalb Ihrer Assembly können nicht davon erben (wird einen Kompilierungsfehler erhalten).

Edit: Wenn Klassen, die können durch externe Baugruppen von MyClass erben zu sehen ist, können Sie nicht MyClass verhindern, dass auch gesehen zu werden - zum Beispiel in Intellisense zeigt nach oben. Sie können jedoch verhindern, dass sie verwendet, indem Sie die obigen Anweisungen befolgen.

+3

Meine Frage geht nicht um Missue. Das habe ich bereits verhindert. Es geht darum, den Intellisense nicht mit einer Klasse zu verschmutzen, die außerhalb der Baugruppe unbrauchbar ist. – m3ntat

+0

@ m3ntat sehe meine bearbeitete Antwort. –

+1

Sie _cannot_ machen Basisklassen weniger zugänglich als abgeleitete Klassen. Sie können die Basisklassenmitglieder (aber nicht die Klasse selbst) stattdessen intern machen. – thecoop

9

Die abstrakte Basisklasse muss öffentlich sein, da die gesamte Vererbungshierarchie für eine Klasse sichtbar sein muss. Dies stellt sicher, dass der Polymorphismus funktioniert und gültig ist; aber alle Mitglieder der Basisklassen können intern sein (einschließlich des Konstruktors) und daher außerhalb Ihrer Assembly nicht verwendbar sein.

+2

Was ist mit dem Downvote? – thecoop

+1

Wenn ich mich nicht irre, hat das nichts mit Polymorphie zu tun: Eine Klasse kann eine interne Schnittstelle implementieren, und diese Schnittstelle in eine abstrakte Klasse umzuwandeln, ändert nichts an der Art, wie die Methoden vererbt werden usw.. Theoretisch könnte man eine Klasse, die eine interne Basisklasse besitzt, gut definieren und in anderen Assemblies verwenden. Es ist nur, dass C# (oder .NET?) Dies nicht erlaubt. –

6

Es gibt wirklich keinen großen Vorteil für das, was Sie erreichen wollen, aber was Sie eigentlich erreichen möchten, ist ähnlich.

Haben Sie Ihre abstrakte Basisklasse in 1 Assembly mit allem internen. In der Assembly für die Montage benötigen Sie

[assembly:InternalsVisibleTo("cs_friend_assemblies_2")] 

Dann in einer anderen Baugruppe Sie die Klassen alle öffentlich zugänglichen wollen, müssen hinzuzufügen. Beachten Sie, dass Sie weiterhin auf die Basisklasse von intellisense für jeden Code innerhalb von cs_friend_assemblies_2 oder wie Sie Ihre Assembly benennen, aber nirgendwo sonst zugreifen können.

+1

Ich denke, das ist der einzige Vorschlag für das OP, um tatsächlich zu erreichen, was er will, obwohl dies eine ziemlich schreckliche Methode ist, aber immer noch +1 – Letseatlunch

+1

Ich könnte keinen Wert in was das OP will erreichen, aber ich würde nicht sagen, das ist eine schreckliche Annäherung. Dies eröffnet grundsätzlich "Friend Assemblies" in .NET. Friend Assemblies, die in falschen Situationen verwendet werden, können zu schrecklichem Code und schrecklichen Frameworks führen, aber das wäre von schlechten Praktiken. Ich finde die meistgenutzte Verwendung von Friend-Assemblys für Testprojekte und manchmal in ServiceLayer-Abhängigkeiten (ex Updated ist {public get; internal protected set} und der Service darf UpdatedBy zuweisen, aber nirgendwo sonst). –

+1

Ich bin auf diese Frage gestoßen, weil ich einen ähnlichen Bedarf an dem OP hatte und fand, dass Ihr der einzige ist, der tatsächlich für das OP arbeiten würde. In meinem Fall würde es jedoch aus bestimmten Gründen nicht funktionieren und das war keine Option. Also würde ich das auch nicht als "Schreckliche" Lösung bezeichnen, das ist eine Übertreibung, aber es fühlt sich eher an wie eine schüchterne Arbeit, die nicht in allen Fällen funktionieren wird. – Letseatlunch

4

Sie können die Klasse nicht gleichzeitig anderen Vererbungen zur Vererbung zur Verfügung stellen, sondern auch privat, damit sie für andere Benutzer nicht sichtbar ist. Sie können die Klasse intern machen und sie einer bestimmten Assembly (wenn es sich um eine Assembly eines Freundes handelt) mit dem Attribut [InternalsVisibleTo] zugänglich machen, aber ich denke nicht, dass dies das ist, was Sie wollen.

Wenn Sie Code (außer abgeleiteten Klassen) behalten wollen von der Möglichkeit, Ihre Basisklasse zu instanziiert, können Sie es einen geschützten Konstruktor geben:

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

Sie können die Klassenmitglieder aus Intellisense verstecken die Verwendung von EditorBrowsable Attribut:

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

Es soll beachtet werden, dass einige Leute Probleme mit den IDE nicht immer dieses Attribut zu respektieren berichtet haben.

+1

EditorBrowsableAttribute ist nur für Klassenmitglieder und nicht für Klassendeklarationen gültig. –

+0

Dies ist, was ich suche, aber es scheint nicht, dies in Intellisense zu respektieren Ich habe es gerade versucht. – m3ntat

+0

Außerdem verhindert das Markieren der Klassenzusammenfassung, dass sie instanziiert wird. Ein geschützter Standardkonstruktor hat dies nicht. –

0

Werden die anderen Assemblies jemals von Ihrer abstrakten Basisklasse oder einer der öffentlichen Klassen erben, die von Ihrer abstrakten Basisklasse erben?

Wenn ja, müssen Sie die abstrakte Basisklasse veröffentlichen. Machen Sie einfach Methoden, die Sie nicht sichtbar außerhalb der Baugruppe intern möchten.

Wenn nicht, können vielleicht Schnittstellen helfen? Definieren Sie öffentliche Schnittstellen, sorgen Sie dafür, dass Ihre öffentlichen Klassen diese implementieren, und stellen Sie eine Factory zum Abrufen von Instanzen bereit. Das einzige, was intellisense außerhalb der Baugruppe sieht, ist die Schnittstelle.

Hilft das?

1

Soweit es mich betrifft, ist dies kein Problem. Beachten Sie:

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

Die oben würde nicht funktionieren ohne Polymorphismus - Lage zu Instanzen verschiedener abgeleiteten Klassen durch ihre gemeinsame Schnittstelle, die abstrakte Basisklasse zu verweisen. Was Sie tun möchten, ist das wegzunehmen und die Benutzerfreundlichkeit Ihrer Klassenhierarchie zu verkrüppeln. Ich denke nicht, dass du diesen Weg weitergehen solltest.

0

Eine Möglichkeit, diese Einschränkung zu umgehen, ist die Verwendung von Komposition statt Vererbung (es gibt othergood reasons, um dies auch zu tun). Zum Beispiel statt:

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

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

tun:

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

Sie können die IMyBase Schnittstelle ganz weglassen, wenn die Öffentlichkeit über sie und Ihre Interna nicht auch nicht zu wissen braucht tun.