tag - propiedades de una clase c#
¿Cómo proteger una propiedad E interna en C#? (7)
Aquí está mi clase abstracta abreviada:
abstract class Report {
protected internal abstract string[] Headers { get; protected set; }
}
Aquí hay 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 deseo poder llamar a Report.Headers
desde cualquier lugar del ensamblado, pero solo permitir que se configure por clases derivadas. Intenté hacer que los Headers
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.
¿Qué hay de malo en hacer público al comprador? Si declara la propiedad como
public string[] Headers { get; protected set; }
cumple con todos los criterios que desea: todos los miembros del ensamblado pueden obtener la propiedad, y solo las clases derivadas pueden configurarlo. Claro, las clases fuera del ensamble también pueden obtener la propiedad. ¿Asi que?
Si realmente necesita exponer la propiedad dentro de su ensamblaje pero no públicamente, otra forma 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 ese prefijo I_
. 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.
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.
El CLR admite el concepto de AND protegido interno (conocido como accesibilidad familiar y de ensamblaje) y C # DEBERÍA implementar / exponer este concepto. C # probablemente debería permitir lo siguiente:
internal string[] Header { get; protected set; }
Hacerlo debe INTERSECT / AND ambos modificadores de visibilidad para el setter de propiedades y le permite leer encabezados desde cualquier lugar dentro del mismo ensamblado pero solo establecerlo desde clases derivadas dentro del mismo ensamblado.
Es una creencia común que no se puede proteger a algunos miembros Y a los internos.
Y es verdad que no puedes 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);
}
}
}
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.
No es posible en C #.
Solo para completar, esto es compatible con IL (familia y modificador de acceso de ensamblaje).
Puede usar una interfaz implementada explícita internamente:
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 obtiene acceso interno en el ensamblado donde se define IReport, que debería ser exactamente lo que desea.
Implementar interfaces explícitamente no es una estrategia bien conocida, pero resuelve muchos problemas.