Pregunta

Aquí está mi acortada clase abstracta:

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, quiero ser capaz de llamar a Report.Headers desde cualquier parte del montaje, pero sólo permitir que sea fijado por las clases derivadas. He intentado hacer Headers simplemente interna, pero protegido que no cuenta como más restrictiva que interna. ¿Hay una manera de hacer las cabeceras internas y su acceso set protegida e internos?

Me siento como si estuviera groseramente mal uso de modificadores de acceso, por lo que cualquier ayuda sería en gran medida el diseño apreciar.

¿Fue útil?

Solución

¿Qué hay de malo en hacer público el comprador? 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. Por supuesto, las clases fuera de la Asamblea pueden obtener la propiedad también. Así?

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

Por supuesto, es feo decorar el nombre con el prefijo I_. Pero es un poco de un diseño extraño. Haciendo algún tipo de nombre mangling en la propiedad interna es una forma de recordarse a sí mismo (o de otros desarrolladores) que la propiedad que están usando es poco ortodoxo. Además, si más adelante decide que la mezcla de la accesibilidad de este tipo no es realmente la solución adecuada a su problema, usted sabrá qué propiedades solucionarlo.

Otros consejos

No es posible en C #.

Sólo por el bien de la integridad, esto es apoyado en IL (acceso montaje familia y modificador).

Me gustaría mantener el modificador de acceso como protegidos y tienen un método de ayuda interna.

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 como debe ser rediseñado de alguna manera. Interna siempre hay algo que haría uso con moderación, ya que puede dar lugar a una arquitectura muy desordenado en el que todo es de alguna manera utilizando todo lo demás dentro del conjunto, pero, por supuesto, siempre hay excepciones.

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

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

Ahora usted consigue el acceso interno en la asamblea donde se define IReport, lo que debe ser realmente lo que desea.

La implementación de interfaces de forma explícita no es una estrategia bien conocida, pero que resuelve un montón de problemas.

El CLR apoya el concepto de protegidas y interna (conocido como la accesibilidad de la familia-y-montaje) y C # DEBE implementado / exponga 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.

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

Es una creencia común que no se puede hacer que algunos miembros protegido o interno.

Y es verdad que no se puede hacerlo de una sola línea, ya que muchos, incluido yo mismo, sería de desear, pero con algo de inteligencia que 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);
        }
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top