2009-06-02 8 views
26

Hier ist meine verkürzte abstrakte Klasse:Wie man eine Eigenschaft geschützt und intern in C#?

abstract class Report { 

    protected internal abstract string[] Headers { get; protected set; } 
} 

Hier ist eine abgeleitete Klasse:

class OnlineStatusReport : Report { 

    static string[] headers = new string[] { 
     "Time", 
     "Message" 
    } 

    protected internal override string[] Headers { 
     get { return headers; } 
     protected set { headers = value; } 
    } 

    internal OnlineStatusReport() { 
     Headers = headers; 
    } 
} 

Die Idee ist, ich will in die von überall Report.Headers in der Lage sein zu können, rufen Assembly, aber nur durch abgeleitete Klassen gesetzt werden. Ich habe versucht, Header nur intern zu machen, aber geschützt gilt nicht als restriktiver als intern. Gibt es eine Möglichkeit, Header intern zu machen und dessen Set Accessor geschützt UND intern?

Ich habe das Gefühl, dass ich Access-Modifikatoren grob missbrauche, also würde jede Design-Hilfe sehr zu schätzen wissen.

+0

Code kompiliert gut für mich. – Noldorin

+3

@Noldorin: geschützt intern ist geschützt ODER intern. –

+0

@Mehrdad: Ja, das wusste ich. Was ist der Punkt? – Noldorin

Antwort

16

Was ist falsch daran, den Getter zu veröffentlichen? Wenn Sie die Eigenschaft als

public string[] Headers { get; protected set; } 

erklären erfüllt es alle Kriterien Sie wollen: alle Mitglieder der Versammlung können die Eigenschaft erhalten, und nur abgeleitete Klassen können es. Sicher, Klassen außerhalb der Versammlung können die Eigenschaft auch erhalten. Damit?

Wenn Sie wirklich die Eigenschaft innerhalb Ihrer Montage aussetzen müssen, aber nicht öffentlich, eine andere Art und Weise zu tun, ist eine andere Eigenschaft zu erstellen:

protected string[] Headers { get; set; } 
internal string[] I_Headers { get { return Headers; } } 

Sicher, es ist hässlich den Namen mit dem I_ Präfix zu dekorieren. Aber es ist irgendwie ein komisches Design. Wenn Sie eine Art von Namensfehlern auf der internen Eigenschaft ausführen, können Sie sich selbst (oder andere Entwickler) daran erinnern, dass die Eigenschaft, die sie verwenden, unorthodox ist. Wenn Sie später entscheiden, dass das Mischen von Zugänglichkeit nicht die richtige Lösung für Ihr Problem ist, werden Sie wissen, welche Eigenschaften zu beheben sind.

+2

Es funktioniert nur, wenn der Typ der 'geschützten' Eigenschaft' public' ist, wie 'string []'. Wenn der Typ der 'protected' Eigenschaft selbst' intern' ist, schlägt die Kompilierung mit der Nachricht 'Inconsistent accessibility' fehl: der Eigenschaftentyp 'Library.A' ist weniger zugänglich als die Eigenschaft 'Library.OnlineStatusReport.Headers'' – DarkWalker

28

In C# ist das nicht möglich.

Nur der Vollständigkeit halber wird dies in IL (Familien- und Baugruppenzugriffsmodifikator) unterstützt.

+0

Gibt es Post-Build-Event-Tools, mit denen der IL-Code nach der Kompilierung geändert werden kann, um das gewünschte Ergebnis zu erzielen? – DarkWalker

6

Ich würde den Zugriffsmodifizierer als geschützt behalten und eine interne Hilfsmethode haben.

protected override string[] Headers { 
    get { return headers; } // Note that get is protected 
    set { headers = value; } 
} 

internal SetHeadersInternal(string[] newHeaders) 
{ 
    headers = newHeaders; 
} 

Aber irgendwie riecht es irgendwie nach Refaktorierung. Intern ist immer etwas, das ich sparsam verwende, weil es zu einer sehr chaotischen Architektur führen kann, in der alles irgendwie alles andere in der Assembly benutzt, aber natürlich gibt es immer Ausnahmen.

2

Es ist eine allgemeine Überzeugung, dass Sie einige Mitglieder nicht sowohl geschützt als auch intern machen können.

Und es ist wahr, dass Sie dies nicht in einer einzigen Zeile tun können, wie viele, einschließlich mir selbst, wünschen, aber mit etwas Klugheit ist es 100% machbar.

//Code below is 100% tested 

/* FROM ProtectedAndInternal.dll */ 

namespace ProtectedAndInternal 
{ 
    public class MyServiceImplementationBase 
    { 
     protected static class RelevantStrings 
     { 
      internal static string AppName = "Kickin' Code"; 
      internal static string AppAuthor = "Scott Youngblut"; 
     } 
    } 

    public class MyServiceImplementation : MyServiceImplementationBase 
    { 
     public void PrintProperties() 
     { 
      // WORKS PERFECTLY BECAUSE SAME ASSEMBLY! 
      Console.WriteLine(RelevantStrings.AppAuthor); 
     } 
    } 

    public class NotMyServiceImplementation 
    { 
     public void PrintProperties() 
     { 
      // FAILS - NOT THE CORRECT INHERITANCE CHAIN 
      // Error CS0122: 'ProtectedAndInternal.MyServiceImplementationBase.Relevant' is inaccessible due to its protection level 
      // Console.WriteLine(MyServiceImplementationBase.RelevantStrings.AppAuthor); 
     } 
    } 
} 



/* From AlternateAssemblyService.dll which references ProtectedAndInternal.dll */ 

namespace AlternateAssemblyService 
{ 
    public class MyServiceImplementation : MyServiceImplementationBase 
    { 
     public void PrintProperties() 
     { 
      // FAILS - NOT THE CORRECT ASSEMBLY 
      // Error CS0117: 'ProtectedAndInternal.MyServiceImplementationBase.RelevantStrings' does not contain a definition for 'AppAuthor' 
      // Console.WriteLine(RelevantStrings.AppAuthor); 
     } 
    } 
} 
+0

Die innere Klasse tut dies nicht müssen statisch sein, um für geschützte interne verwendet zu werden. Ich habe Ihre Idee verwendet, aber in der Elternklasse hatte ich Instanzen der inneren Klasse, mit denen ich auf meine "geschützten internen" Mitglieder zugegriffen habe. –

5

Sie könnten eine interne explizit implementierte Schnittstelle verwenden:

internal interface IReport 
{ 
    string[] Headers { get; } 
} 

abstract class Report : IReport 
{ 
    protected abstract string[] Headers { get; protected set; } 

    string[] IReport.Headers 
    { 
     get { return Headers; } 
    } 
} 

class OnlineStatusReport : Report 
{ 
    static string[] headers = new string[] { "Time", "Message" }; 

    protected internal override string[] Headers 
    { 
     get { return headers; } 
     protected set { headers = value; } 
    } 

    internal OnlineStatusReport() 
    { 
     Headers = headers; 
    } 
} 

Jetzt erhalten Sie den internen Zugriff in der Montage, wo IReport definiert ist, die genau sein sollte, was Sie wollen.

Die explizite Implementierung von Schnittstellen ist keine bekannte Strategie, löst aber viele Probleme.

3

Die CLR unterstützt das Konzept der geschützten UND internen (bekannt als Familie und Baugruppenzugänglichkeit) und C# sollte dieses Konzept implementiert/verfügbar machen. C# sollte wohl erlauben, die folgenden:

internal string[] Header { get; protected set; } 

Dabei sollte INTERSECT/und beide Sichtbarkeitsmodifizierer für die Eigenschaft Setter und ermöglichen es Ihnen Headers von jedem beliebigen Ort innerhalb derselben Baugruppe zu lesen, aber nur setzen Sie ihn von abgeleiteten Klassen innerhalb derselben Baugruppe .

+0

Die CLR unterstützt das Konzept der geschützten AND-internen (bekannt als Familie und Montage Zugänglichkeit) und meine Antwort oben schlägt vor, dass C# dieses Konzept implementiert/verfügbar machen und interpretieren die vorgeschlagene Syntax als solche. Beachten Sie, dass ich den internen Modifikator auf die Eigenschaft und den geschützten Modifikator auf den Setter setze (nicht den gleichen, indem ich sowohl "protected internal" auf den Setter setze). Danke fürs Kommentieren, ich habe meine Antwort bearbeitet, um diese Idee zu betonen und zukünftige Verwirrungen zu vermeiden. –

+0

Es sollte in irgendeiner Weise unterstützen ... aber ich bin mir sicher, nicht wie es hier ist ... versuchen, interne UND geschützten Konstruktor vorschlagen?Konstruktor, auf den nur von der Bibliothek (Assembly) zugegriffen werden sollte und auf den von keinem abgeleiteten Typ in anderen Bibliotheken zugegriffen werden sollte. Vielleicht können wir unterscheiden "geschützte interne" und "interne geschützte" Modifikatoren. Also 2. bedeutet, dass es zuerst "intern" ist und erst dann "geschützt". Und Original bedeutet in jeder abgeleiteten Klasse ("geschützt") und auch überall intern (wie es jetzt ist). – Maxim

1

Seit C# 7.2 gibt es Konstrukt private protected (link). Es erlaubt nicht, vom Feld zu lesen (macht also nicht genau, was das OP beabsichtigt), aber es lohnt sich, eine Beute zu nehmen.

+0

Es könnte tatsächlich lesen, wenn in Kombination mit 'internen' verwendet: 'interne Überschreibung string [] Headers {get {return headers; } private protected set {headers = Wert; }} 'wäre internal-only read und internal-and-protected-only write. Andere Kombinationen wären auch möglich. –

Verwandte Themen