2009-06-02 18 views
26

Aquí está mi clase abstracta acortada:¿Cómo hacer una propiedad protegida E interna en C#?

abstract class Report { 

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

Aquí es una clase derivada:

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

La idea es que quieren ser capaces de poder llamar Report.Headers desde cualquier lugar del ensamblado, pero solo permite que sea configurado por clases derivadas. Intenté hacer que los encabezados fueran internos, pero protegidos no cuentan como más restrictivos que los internos. ¿Hay alguna manera de hacer que los Encabezados sean internos y que su acceso de acceso esté protegido Y sea interno?

Siento que estoy haciendo un uso indebido de los modificadores de acceso, por lo que cualquier ayuda de diseño sería muy apreciada.

+0

El código compila bien para mí. – Noldorin

+3

@Noldorin: protegido interno está protegido O interno. –

+0

@Mehrdad: Sí, lo sabía. ¿Cuál es el punto de? – Noldorin

Respuesta

16

¿Qué hay de malo en hacer que el captador sea público? Si se declara la propiedad como

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

cumple con todos los criterios que desee: todos los miembros de la asamblea puede obtener la propiedad, y sólo las clases derivadas pueden configurarlo. Claro, las clases fuera del ensamble también pueden obtener la propiedad. ¿Asi que?

Si realmente necesita para exponer la propiedad dentro de su ensamblaje, pero no públicamente, otra manera de hacerlo es crear una propiedad diferente:

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

Claro, es feo decorar el nombre con el que I_ prefijo. Pero es un tipo de diseño extraño. Hacer algún tipo de nombre manipulando la propiedad interna es una forma de recordarse (u otros desarrolladores) que la propiedad que están usando es poco ortodoxa. Además, si posteriormente decide que la mezcla de accesibilidad como esta no es realmente la solución correcta para su problema, sabrá qué propiedades corregir.

+2

Funciona solo si el tipo de propiedad 'protected' es' public', como 'string []'. Si el tipo de propiedad 'protected' es en sí mismo 'internal', la compilación falla con el mensaje' Inconsistent accesibility: tipo de propiedad 'Library.A' es menos accesible que la propiedad 'Library.OnlineStatusReport.Headers'' – DarkWalker

28

No es posible en C#.

Para completar, esto es compatible con IL (familia y modificador de acceso de ensamblaje).

+0

¿Existen herramientas posteriores al evento de compilación que permitan modificar el código IL después de la compilación para obtener el resultado deseado? – DarkWalker

6

Mantendría el modificador de acceso como protegido y tengo un método interno de ayuda.

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

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

Pero de alguna manera, esto huele a que debería ser refactorado de alguna manera. Interno siempre es algo que usaría con moderación porque puede conducir a una arquitectura muy desordenada donde todo está de alguna manera usando todo lo demás dentro del ensamblaje, pero por supuesto siempre hay excepciones.

2

Es una creencia común que no se puede proteger internamente a algunos miembros.

Y es cierto que no puede hacerlo en una sola línea, como muchos, incluyéndome a mí, desearían, pero con cierta astucia es 100% factible.

//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 clase interna no tiene que ser estático para ser utilizado para interno protegido. Usé tu idea, pero en la clase para padres tuve instancias de la clase interna que utilicé para acceder a mis miembros "protegidos internos". –

5

Se podría utilizar un interfaz interna explícita implementado:

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

ya que usted consigue el acceso interno en la asamblea donde se define IReport, que debe ser exactamente lo que quiere.

Implementar interfaces explícitamente no es una estrategia bien conocida, pero resuelve muchos problemas.

3

El CLR admite el concepto de interno protegido AND (conocido como accesibilidad de familia y ensamblaje) y C# DEBERÍA implementar/exponer este concepto. C#, probablemente debería permitir lo siguiente:

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

Si lo hace, está cortado/Y ambos modificadores de visibilidad para el colocador propiedad y permitirá leer encabezados desde cualquier lugar dentro del mismo conjunto, pero sólo establece que a partir de las clases derivadas dentro del mismo conjunto .

+0

El CLR admite el concepto de AND protegido interno (conocido como accesibilidad de familia y ensamblaje) y mi respuesta anterior sugiere que C# DEBERÍA implementar/exponer este concepto e interpretar la sintaxis propuesta como tal. Observe que puse el modificador interno en la propiedad y el modificador protegido en el setter (no es lo mismo al poner ambos "internos protegidos" en el setter). Gracias por comentar, he editado mi respuesta para enfatizar esta idea y evitar confusiones futuras. –

+0

Debería ser compatible de alguna manera ... pero estoy seguro de que no es así ... ¿intenta sugerir el constructor interno Y protegido?constructor al que se debe acceder solo desde la biblioteca (ensamblado) y no se debe acceder desde ningún tipo derivado en otras bibliotecas. Tal vez podamos diferenciar los modificadores "protegido interno" y "protegido interno". Entonces, el segundo significa que es "interno" primero y solo entonces "protegido". Y los medios originales están disponibles en cualquier clase derivada ("protegida") y también en todas partes internamente (como lo es ahora). – Maxim

1

Desde C# 7.2 hay construcción private protected (link). No permite la lectura desde el campo (por lo tanto, no hace exactamente lo que el OP intenta), pero vale la pena tomar un botín.

+0

De hecho, podría permitir la lectura si se utiliza en combinación con 'internal':' cadena de anulación interna [] Encabezados {get {return headers; } conjunto privado protegido {encabezados = valor; }} 'sería lectura solo interna y escritura solo interna y protegida. Otras combinaciones también serían posibles. –

Cuestiones relacionadas