¿Por qué no puedo acceder a los miembros protegidos de C #, excepto de esta manera?

Este código:

abstract class C { protected abstract void F(D d); } class D : C { protected override void F(D d) { } void G(C c) { cF(this); } } 

Genera este error:

No se puede acceder al miembro protegido ‘CF (D)’ a través de un calificador de tipo ‘C’; el calificador debe ser del tipo ‘D’ (o derivado de él)

¿En qué demonios estaban pensando? (¿Alteraría esa regla para romper algo?) ¿Y hay una forma de evitar eso además de hacer que F sea público?


Edición: ahora entiendo la razón de por qué esto es (gracias Greg ) pero todavía estoy un poco perplejo en cuanto a lo racional; dado:

 class E : C { protected override void F(D d) { } } 

¿Por qué D no debería ser capaz de llamar a EF?


El mensaje de error está editado, así que podría haber puesto un error tipográfico allí.

    La palabra clave “protegida” significa que solo un tipo y tipos que derivan de ese tipo pueden acceder al miembro. D no tiene relación con C, por lo tanto, no puede acceder al miembro.

    Tienes un par de opciones si quieres poder acceder a ese miembro

    • Hazlo público
    • Hazlo interno. Esto permitirá que cualquier tipo acceda al miembro dentro del mismo conjunto (u otras asambleas si agrega amigo)
    • Derive D desde C

    EDITAR

    Este escenario se llama en la sección 3.5.3 de la especificación de C #.

    La razón por la que no está permitido es porque permitiría llamadas de jerarquía cruzada. Imagine que además de D, había otra clase base de C llamada E. Si su código pudiera comstackr, permitiría a D acceder al miembro EF Este tipo de escenario no está permitido en C # (y creo que el CLR pero no lo hago) t 100% saber).

    EDIT2 ¿ Por qué esto es malo?

    Advertencia, esta es mi opinión

    La razón por la que ahora está permitido es porque es muy difícil razonar sobre el comportamiento de una clase. El objective de los modificadores de acceso es dar al desarrollador control sobre quién puede acceder a métodos específicos. Imagina la siguiente clase

     sealed class MyClass : C { override F(D d) { ... } } 

    Considere lo que sucede si F es una función algo crítica en el tiempo. Con el comportamiento actual, puedo razonar sobre la corrección de mi clase. Después de todo, solo hay dos casos donde se llamará MyClass.F.

    1. Donde se invoca en C
    2. Donde lo invoco explícitamente en MyClass

    Puedo examinar estas llamadas y llegar a una conclusión razonable sobre cómo funciona MyClass.

    Ahora bien, si C # permite el acceso protegido por jerarquía cruzada, no puedo ofrecer tal garantía. Cualquier persona en un ensamble completamente diferente puede venir y derivar de C. Entonces pueden llamar MyClass.F a voluntad. Esto hace que sea completamente imposible razonar sobre la corrección de mi clase.

    La razón por la que esto no funciona es porque C # no permite la llamada entre jerarquías de métodos protegidos. Digamos que había una clase E que también derivaba de C :

      C / \ DE 

    Entonces, la referencia a la que intenta llamar el método podría ser una instancia de tipo E y, por lo tanto, el método podría resolverse en tiempo de ejecución a EF . Esto no está permitido en C # ya que D no puede llamar a los métodos protegidos de E , porque E está en otra twig de la jerarquía, es decir,

     var d = new D(); var e = new E(); dG(e); // oops, now this will call EF which isn't allowed from D 

    Esto tiene sentido porque la palabra clave protected significa que el miembro ” es accesible dentro de su clase y por instancias de clase derivadas ” y EF no es miembro de D.

    Aunque D hereda de C, D no puede acceder a los miembros protegidos de C. D puede acceder a los miembros protegidos de D'(¡y privados!), De modo que si pone otra instancia de D allí en lugar de C, todo funcionaría. Pero como dijo Greg, C realmente podría ser algo completamente diferente, y como el comstackdor no sabe qué es C en realidad, tiene que evitar que D acceda a algo a lo que D no puede realmente acceder.

    Una serie de publicaciones que explican esto desde la perspectiva del comstackdor de C #:

    • ¿Por qué no puedo acceder a un miembro protegido de una clase derivada?
    • ¿Por qué no puedo acceder a un miembro protegido de una clase derivada, segunda parte: ¿por qué puedo hacerlo?

    Esta limitación se puede evitar utilizando un método protegido estático:

     abstract class C { protected abstract void F (D d); // Allows calling F cross-hierarchy for any class derived from C protected static void F (C c, D d) { cF(d); } } class D : C { protected override void F (D d) { } void G (C c) { // cF(this); F(c, this); } } 

    Esto no es perfecto desde el punto de vista de la seguridad (cualquiera puede derivar de C ), pero si todo lo que le importa es ocultar el método F de la interfaz pública de la clase C , este truco puede ser útil.

    Para entender por qué este tipo de comportamiento tiene sentido, consideremos por qué necesitamos modificadores de acceso en todos los lenguajes de progtwigción orientados a objetos. Necesitamos que limiten un ámbito en el que se pueda usar un miembro de clase particular . Y eso a su vez simplifica la búsqueda de los usos.

    Para resumir:

    • para encontrar todos los usos del miembro público uno necesita buscar a través de todo el proyecto (y esto no es suficiente en el caso de la biblioteca que es utilizada por desarrolladores independientes)
    • Para encontrar todos los usos del miembro protegido , uno debe buscar a través de la clase contenedora y todas sus subclases
    • para encontrar todos los usos del miembro privado uno necesita buscar a través de la clase contenedora .

    Entonces, si el comstackdor permitió llamar al método protegido desde la superclase de la manera descrita, podríamos terminar con una llamada de jerarquía cruzada de métodos protegidos como se describe en esta respuesta . Y en tal situación uno tenía que buscar entre todos los niños de la clase más padres que define al miembro. Y eso boostía el scope.

    PD. El mismo comportamiento se implementa en Java.

    Sí, es posible. Probablemente tendremos un ejemplo así muy pronto.

    Para hacer eso debes hacer lo siguiente:

    1. Herede el formulario predeterminado (EditAppointmentDialog) y realice su personalización (incluso puede usar el diseñador de winforms para eso).

    public partial class CustomAppointmentEditDialog: EditAppointmentDialog {private RadComboBox cmbShowTimeAs = null;

      public CustomAppointmentEditDialog() { InitializeComponent(); this.cmbShowTimeAs = this.Controls["cmbShowTimeAs"] as RadComboBox; } private void chkConfirmed_ToggleStateChanged(object sender, StateChangedEventArgs args) { this.cmbShowTimeAs.SelectedValue = (args.ToggleState == ToggleState.On) ? (int)AppointmentStatus.Busy : (int)AppointmentStatus.Tentative; } } 

    En el código anterior he agregado una checkbox adicional y establezco el estado (mostrar hora como) de la cita en Provisional si está desmarcada o en Ocupado si está marcada. La extraña forma de acceder al cuadro combinado es porque actualmente es privado. Esto se modificará para la próxima versión del primer trimestre de 2009.

    1. Suscríbete al evento AppointmentEditDialogShowing de RadScheduler y sustituye el formulario predeterminado por el personalizado:

    private IEditAppointmentDialog appointmentEditDialog = null;

      protected override void OnLoad(EventArgs e) { base.OnLoad(e); this.radScheduler1.AppointmentEditDialogShowing += new EventHandler(radScheduler1_AppointmentEditDialogShowing); } void radScheduler1_AppointmentEditDialogShowing(object sender, Telerik.WinControls.UI.AppointmentEditDialogShowingEventArgs e) { if (this.appointmentEditDialog == null) { this.appointmentEditDialog = new CustomAppointmentEditDialog(); } e.AppointmentEditDialog = this.appointmentEditDialog; } 

    Espero que esto ayude. No dude en escribirme si tiene más preguntas.