2009-06-02 8 views
26

Qui è la mia classe astratta accorciato:Come rendere protetta una proprietà E interna in C#?

abstract class Report { 

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

Ecco una classe derivata:

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

L'idea è, voglio essere in grado di essere in grado di chiamare Report.Headers da qualsiasi parte del assembly, ma consente solo di essere impostato da classi derivate. Ho provato a creare intestazioni interne, ma la protezione non è più restrittiva che interna. C'è un modo per rendere le intestazioni interne e il suo set accessor protetto E interno?

Mi sento come se stessi abusando grossolanamente di modificatori di accesso, quindi qualsiasi aiuto di progettazione sarebbe molto apprezzato.

+0

Il codice compila bene per me. – Noldorin

+3

@Noldorin: interno protetto è protetto O interno. –

+0

@ Mehrdad: Sì, lo sapevo. Qual e il punto? – Noldorin

risposta

16

Cosa c'è di sbagliato nel rendere pubblico il getter? Se si dichiara la proprietà come

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

soddisfa tutti i criteri desiderati: tutti i membri dell'assemblea può ottenere la proprietà, e solo le classi derivate possibile impostarlo. Certo, le lezioni al di fuori dell'assemblea possono ottenere anche la proprietà. Così?

Se avete veramente bisogno di esporre la proprietà all'interno della vostra assemblea, ma non pubblicamente, un altro modo per farlo è quello di creare una proprietà diversa:

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

Certo, è brutto decorare il nome a quello I_ prefisso. Ma è una specie di design strano. Fare un qualche tipo di manomissione dei nomi sulla proprietà interna è un modo per ricordare a te stesso (o agli altri sviluppatori) che la proprietà che stanno usando non è ortodossa. Inoltre, se in seguito decidi che il mixaggio dell'accessibilità come questa non è la soluzione giusta per il tuo problema, saprai quali proprietà correggere.

+2

Funziona solo se il tipo di proprietà 'protected' è' public', come 'string []'. Se il tipo di proprietà 'protected' è esso stesso' internal' - la compilazione fallisce con il messaggio 'Accessibilità incoerente: il tipo di proprietà 'Library.A' è meno accessibile della proprietà 'Library.OnlineStatusReport.Headers'' – DarkWalker

28

Non è possibile in C#.

Solo per motivi di completezza, questo è supportato in IL (modificatore di accesso per famiglie e assiemi).

+0

Esistono strumenti per eventi post-generazione che consentono di modificare il codice IL dopo la compilazione per ottenere il risultato desiderato? – DarkWalker

6

Manterrei il modificatore di accesso come protetto e avrò un metodo di supporto interno.

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

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

Ma in qualche modo, questo puzza come dovrebbe essere refactored in qualche modo. L'interno è sempre qualcosa che userei con parsimonia perché può portare a un'architettura molto caotica in cui tutto sta in qualche modo usando tutto il resto all'interno dell'assembly, ma ovviamente ci sono sempre delle eccezioni.

2

È opinione comune che non sia possibile rendere alcuni membri sia protetti che interni.

Ed è vero che non è possibile farlo in una sola riga, come molti, incluso me stesso, vorrebbe, ma con un po 'di intelligenza è al 100% in grado.

//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

La classe interna non deve essere statico per essere utilizzato per interni protetti. Ho usato la tua idea, ma nella classe genitore ho avuto istanze della classe interiore che ho usato per accedere ai miei membri "protetti interni". –

5

È possibile usare un interno esplicito interfaccia implementata:

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

Ora si ottiene accesso interno nel gruppo in cui è definita IReport, che dovrebbe essere esattamente quello che vuoi.

L'implementazione di interfacce esplicitamente non è una strategia ben nota, ma risolve un sacco di problemi.

3

Il CLR supporta il concetto di E interno protetto (noto come accessibilità famiglia e assemblaggio) e C# DOVREBBE implementare/esporre questo concetto. C# dovrebbe probabilmente consentire la seguente:

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

In questo modo dovrebbe INTERSECT/E sia modificatori di visibilità per il setter di proprietà e consentono di leggere le intestazioni da qualsiasi punto all'interno della stessa assemblea, ma solo imposta dalle classi derivate all'interno della stessa assemblea .

+0

Il CLR supporta il concetto di E interno protetto (noto come accessibilità famiglia e assemblaggio) e la mia risposta sopra suggerisce che C# DOVREBBE implementare/esporre questo concetto e interpretare la sintassi proposta come tale. Notate che ho messo il modificatore interno sulla proprietà e il modificatore protetto sul setter (non è lo stesso a mettere entrambi i "protetti interni" sul setter). Grazie per il commento, ho modificato la mia risposta per sottolineare questa idea ed evitare confusioni future. –

+0

Dovrebbe supportare in qualche modo ... ma non sono sicuro che sia qui ... prova a suggerire un costruttore interno e protetto?costruttore che dovrebbe essere accessibile solo dalla libreria (assembly) e non dovrebbe essere accessibile da qualsiasi tipo derivato in altre librerie. Forse possiamo differenziare i modificatori "protetti interni" e "protetti internamente". Quindi il 2 ° significa che è "interno" prima e solo poi "protetto". E mezzi originali disponibili in qualsiasi classe derivata ("protetta") e ovunque all'interno (come ora). – Maxim

1

Poiché C# 7.2 è costruito private protected (link). Non consente la lettura dal campo (quindi non fa esattamente ciò che l'OP intende), ma vale la pena prendere un bottino.

+0

Potrebbe effettivamente consentire la lettura se usato in combinazione con 'internal':' override interno stringa [] Intestazioni {get {return headers; } set protetto privato {intestazioni = valore; }} 'sarebbe di sola lettura interna e solo di scrittura interna e protetta. Altre combinazioni sarebbero possibili anche. –