2010-05-16 18 views
29

tengo el siguiente código:¿Debería una propiedad protegida en una clase secundaria C# ocultar el acceso a una propiedad pública en el elemento primario?

public class Parent 
{ 
    public string MyField { get; set; } 
} 

public class Child : Parent 
{ 
    protected new int MyField { get; set; } 
} 

Trato de acceder a este con:

static void Main(string[] args) 
{ 
    Child child = new Child(); 
    child.MyField = "something"; 
} 

Visual Studio 2008 compila esto sin comentario, pero bajo Mono (2.4.2, Ubuntu) consigo el mensaje de error

'HideTest.Child.MyField' is inaccessible due to its protection level (CS0122) 

¿Hay una implementación u otra que cumpla más con el estándar aquí?

Edit: Gracias a todas las personas que han señalado el mal diseño. Lamentablemente, es una biblioteca de terceros y cambiarla significativamente no es práctico.

+0

¿Esto realmente tiene algo que ver con la herencia? Ubuntu funciona con cualquier campo protegido en la clase Child? –

+2

Antes de responder, ¿puede explicarme cómo una propiedad puede ser pública en la clase de padres y protegida en la clase de niños? Usted tiene un serio problema de diseño, creo. –

+3

usando 'nuevo' es un error que debe evitarse, especialmente cuando tiene el control de la fuente para ambas clases. Y lo que intenta hacer, es decir, cubrir una propiedad heredada con un tipo diferente, huele realmente mal. el pescado podrido apesta, incluso. ;-) Deberías echar un vistazo a lo que intentas lograr y encontrar una manera limpia de hacerlo. La formación de malos hábitos como este temprano en la vida dará lugar a ataques de psoriasis y ansiedad. –

Respuesta

25

De ECMA-334 (la especificación C#) §10.7.1.2:

A declaration of a new member hides an inherited member only within the scope of the new member.

se puede ver este comportamiento mediante la ejecución de esta prueba en la implementación de Microsoft.

using System; 
using NUnit.Framework; 

namespace ScratchPad 
{ 
    [TestFixture] 
    public class Class1 
    { 
     [Test] 
     public void InheritanceHiding() 
     { 
      var b = new Base(); 
      var d = new Derived(); 

      var baseSomeProperty = b.SomeProperty; 
      var derivedSomeProperty = d.SomeProperty; 

      b.GetSomeProperty(); 
      d.GetSomeProperty(); 
     } 
    } 

    public class Base 
    { 
     public string SomeProperty 
     { 
      get 
      { 
       Console.WriteLine("Getting Base.SomeProperty"); 
       return "Base.SomeProperty"; 
      } 
     } 

     public string GetSomeProperty() 
     { 
      return SomeProperty; 
     } 
    } 

    public class Derived : Base 
    { 
     protected new int SomeProperty 
     { 
      get 
      { 
       Console.WriteLine("Getting Derived.SomeProperty"); 
       return 3; //Determined by random roll of the dice. 
      } 
     } 

     public new int GetSomeProperty() 
     { 
      return SomeProperty; 
     } 
    } 
} 

Cuál sería:

Getting Base.SomeProperty //(No Controversy) 
Getting Base.SomeProperty //(Because you're calling from public scope and the new member is in protected scope, there is no hiding) 
Getting Base.SomeProperty //(No Controversy) 
Getting Derived.SomeProperty //(Now because you're calling from protected scope, you get the protected member). 

Así que la propiedad que usted está accediendo desde su Main() debe ser la característica de la clase base (como lo es en MS.NET), no la propiedad derivada (como se en Mono), porque el nuevo miembro derivado solo oculta el 'antiguo' miembro base en el alcance protegido.

Mono está haciendo algo mal aquí de acuerdo con la especificación.

+0

@Eric Lippert: Aunque creo que esto es auto consistente, algunos podrían pensar que hay una disonancia aquí. ¿Le importaría agregar una explicación de por qué el comportamiento de MS.NET es mejor que el comportamiento de Mono? Mi cerebro no puede dar con el por qué esto es bueno en este momento y creo que eso haría la respuesta más completa ... –

+0

Excelente pregunta. Vea abajo. –

0

mi humilde opinión, la diferencia es que MS.NET reconoce el tipo cadena de MyField y establece el valor de la propiedad y en Parent Mono en tan sólo intenta acceder MyFieldChild en clase.

0

Está haciendo que algo disponible a través de la clase base no esté disponible a través del niño. Puedes intentarlo, pero en realidad no hará nada. La gente siempre puede hacer esto:

Parent child = new Child();

y llama al método. Entonces, si desea que el campo esté oculto, declare uno nuevo y mantenga el público heredado.

5

En general, la implementación .NET de C# probablemente debería considerarse "canon". A partir de la documentación sobre el new Modifier:

A constant, field, property, or type introduced in a class or struct hides all base class members with the same name.

... parece que la aplicación Mono es más correcto dada esta definición. Debería estar ocultando la implementación de MyField en la clase Parent y, por lo tanto, solo debería poder accederse con la firma int MyField de la clase Child.

+0

Su cita es absolutamente correcta, pero otra parte de la especificación entra en juego (ver mi respuesta). –

+0

Ah, buena explicación. El documento de MSDN probablemente podría ser un poco más claro en esto. –

3

Preludio: Este código es una locura. Si realmente tienes un código en tu aplicación como este, arréglelo ahora. ¡O haz que ambos estén protegidos o sean públicos!

En cuanto al error: El CLR tiene muchas reglas realmente extrañas de "borde del caso" para tratar con cosas como esta. El mejor lugar para buscar este tipo de cosas suele ser Eric Lippert's blog.

Al decir que, sin embargo, parece que mono realmente está haciendo lo más sensato aquí en mi opinión.


En segundo lugar, el C# tiene más sentido una vez que se toma en cuenta lo de "detrás de escena".

Las propiedades no son de "primera clase" en MSIL. Una propiedad en C# o VB simplemente se compila en un método get y set (el compilador también pega un atributo en algún lugar para la contabilidad).

int MyField { get; set; } producirá realmente MSIL por dos métodos:

void set_MyField(int value); 
int get_MyField(); 

Ahora, dado que su método new tiene un tipo diferente, que va a terminar con los siguientes 2 métodos setter.

void set_MyField(int value); 
void set_MyField(string value); 

Cuando se llama a x.MyField = "string" Estás a llamar a uno de esos métodos. Esto se reduce a un escenario de sobrecarga de método normal. Es perfectamente válido tener dos métodos con el mismo nombre que tomen diferentes parámetros, por lo que el compilador simplemente seleccionará la cadena uno y continuará de manera feliz.

Así que sí. El C# uno tiene sentido si sabes cómo funcionan las partes internas, el Mono tiene más sentido si no lo haces.

¿Cuál es "más correcto"? Pedir a Eric Lippert :-)

+0

No es mi código, es una biblioteca de terceros. El código real es menos extraño, ya que no tiene diferentes tipos para las diferentes propiedades (eso fue algo que introduje para aclarar lo que estaba pasando), y las dos versiones de la propiedad comparten la implementación. IIRC el tipo de la propiedad en la clase derivada es una subclase del tipo de la propiedad en la clase padre. –

18

La respuesta de Jason es correcta, pero él pide una justificación de este comportamiento. (A saber, que un método de ocultación solo se oculta dentro del alcance del método de ocultación).

Existen varias justificaciones posibles. Uno en particular es ese this is yet another way in which the design of C# mitigates the Brittle Base Class problem.

FooCorp hace Foo.dll:

public class Foo 
{ 
    public object Blah() { ... } 
} 

BarCorp hace bar.dll:

public class Bar : Foo 
{ 
    // stuff not having to do with Blah 
} 

ABCCorp hace abc.exe:

public class ABC 
{ 
    static void Main() 
    { 
     Console.WriteLine((new Bar()).Blah()); 
    } 
} 

Ahora BarCorp dice "Usted sabe, en nuestro código interno podemos garantizar que Blah solo devuelve cadena gracias a nuestro conocimiento de nuestra implementación derivada. Vamos a tomar e ventaja de ese hecho en nuestro código interno ".

public class Bar : Foo 
{ 
    internal new string Blah() 
    { 
     object r = base.Blah(); 
     Debug.Assert(r is string); 
     return (string)r; 
    } 
} 

ABCCorp recoge una nueva versión de bar.dll que tiene un montón de correcciones de errores que les están bloqueando. ¿Debería romper su compilación porque tienen una llamada a Blah, un método interno en la barra? Por supuesto no. Eso sería terrible.Este cambio es un detalle de implementación privada que debe ser invisible fuera de Bar.DLL.

+15

Sabes que estás teniendo un buen día cuando Eric Lippert te dice que tienes razón. Gracias por responder la señal del bate :) –

2

Simplemente agregando mis 2 centavos) Eso es un error Mono, here es la descripción.

Cuestiones relacionadas