2009-04-16 41 views
6

¿Debería el siguiente código dar una advertencia?¿Por qué mi método estático oculta mi método de instancia?

class Foo { public void Do() { /*...*/ } /*...*/ } 
class Bar : Foo { public static void Do() { /*...*/ } /*...*/ } 

Da:

"CS0108 Advertencia: 'Bar.Do()' esconde miembro heredada 'Foo.Do()' Utilice la nueva palabra clave si se pretendía ocultar.".

Si hago un cambio en el código:

class Foo { public static void Do() { /*...*/ } /*...*/ } 
class Bar : Foo { public void Do() { /*...*/ } /*...*/ } 

tengo la misma advertencia.

Si realizo el siguiente cambio, sin embargo, la advertencia desaparece.

class Foo { public void Do() { /*...*/ } /*...*/ } 
class Bar : Foo { new public static void Do() { /*...*/ } /*...*/ } 

Permítanme hacer un nuevo cambio:

class Foo { public void Do() { /*...*/ } /*...*/ } 
class Bar : Foo { 
    new public static void Do() 
    { new Bar().Do();/*...*/ } /*...*/ 
} 

Esto no se compila:

"CS0176 de error: Miembro 'Bar.Do()' no se puede acceder con una instancia referencia, califíquelo con un nombre de tipo en su lugar ".

¡Así que pierdo el acceso a mi método heredado a través de una referencia de instancia desde un método estático!

¿Cuál sería la lógica detrás de esto? ¿O hice un error tipográfico en alguna parte?

Me encontré con esto cuando estaba tratando de definir un método estático 'Mostrar' para mi formulario derivado de 'Forma'.

Respuesta

5

¿Dónde crees que es el error? El hecho de que haya una advertencia es absolutamente correcto. A partir de la especificación C# 3.0, sección 10.3.4:

Una clase-miembro-declaración es permitida para declarar un miembro de la mismo nombre o firma como un miembro de heredada.Cuando esto ocurre, se dice que el miembro derivado de la clase oculta el miembro base de la clase . Ocultación de un miembro de heredada no se considera un error, pero sí causa que el compilador emita una advertencia . Para suprimir la advertencia, la declaración del miembro de clase derivada puede incluir un nuevo modificador a indica que el miembro derivada es destinado a ocultar el elemento de base.

El hecho de que su invocación del método no es más sutil, pero es básicamente porque el algoritmo de búsqueda de miembro recoge el método estático, y luego se utiliza esta parte de la sección 7.5.5.1:

La validación final de el elegido mejor método se realiza:

el método se validó en el contexto del grupo método : Si el mejor método es un método estático , el grupo método debe tener resultó de un nombre simple o un acceso de miembro a través de un tipo. Si el mejor método es un método de instancia, el grupo de métodos debe haber resultado de un nombre simple , un acceso de miembro a través de una variable o valor , o un acceso base. Si ninguno de estos requisitos es verdadero, se produce un error en tiempo de compilación.

+0

Hola Jon, +1 por respuesta. Creo que, desde la perspectiva del usuario del lenguaje, el error está en el camino de elegir el mejor método para llamar :-). Para mí, "new Bar(). Do()" indica claramente qué método se está llamando en el momento de la compilación y no es necesario producir un error. – isntn

+1

Sospecho que la alternativa es hacer el lenguaje mucho más complicado. Con bastante frecuencia hay casos extremos que podrían mejorarse si el lenguaje fuera más inteligente, pero sería más difícil "conocer" bien el lenguaje y también implementarlo correctamente en un compilador. Todo es equilibrio ... –

2

No, eso tiene mucho sentido. Esto funciona como se esperaba:

using System; 
using System.Collections.Generic; 

class Foo { public void Do() { /*...*/ } /*...*/ } 
class Bar : Foo { 
    new public static void Do() 
    { ((Foo)new Bar()).Do();/*...*/ } /*...*/ 
} 

Eso es porque el compilador supone que tiene un tipo de bar, y luego encuentra el miembro estático. Al enviarlo a Foo (que viene gratis por cierto) lo haces ver en la metdadata para Foo() y todo está bien.

+0

cuando digo "La nueva barra() No();". Seguramente estoy llamando al método de instancia "Do", que está disponible en Bar vía Foo. Entonces, ¿por qué tenemos que escribir elenco? – isntn

+1

Porque el compilador no mira a la instancia, sino a los metadatos de tipo. Y aquí solo encuentra un método con la firma correcta (nombre y lista de argumentos vacíos), y ese es el estático. – Lucero

+0

Piense en ello en términos de tablas de reducción virtuales; el mapeo Bar :: Do() oculta el mapeo de Foo: Do ​​(). –

2

Prueba esto:

 new public static void Do() 
     { 
      ((Foo)new Bar()).Do(); 
     } 
0

En el ejemplo de código final, la nueva palabra clave en la declaración de Bar.Do() significa que la intención de ocultar Foo.Do().

0

Por curiosidad, ¿por qué quieres hacer esto? Parece que probablemente estás tomando tu solución de la manera incorrecta.

El código anterior parece funcionar como debería y los mensajes del compilador le informan cuáles son los problemas.

+0

Hola Josh, no "quiero" hacerlo. Es solo que me encuentro con esto y quiero entender este comportamiento elegido por los diseñadores de C#. "new Bar(). Do()" dice obviamente que se está llamando al método de instancia. Entonces, ¿por qué C# está diseñado para hacer esto aún más explícito? un poco de pregunta educativa. – isntn

+0

Ya veo. Bueno, en ese caso, debes darte cuenta de que en Bar, hay un método de instancia * no * llamado Do(), solo un método estático. Al darle el mismo nombre, ha bloqueado la herencia. Así que no, no es obvio que el método de instancia se está llamando, para mí o para el compilador = p – JoshJordan

+0

Creo que hay un método de instancia en Bar que se deriva de Foo. – isntn

1

usted debe ser capaz de llamar al hacerlo llamando base.Do() en lugar de Bar() No()

Cuestiones relacionadas