overload method c# virtual-method overload-resolution

method overload c#



Resolución de sobrecarga y métodos virtuales (5)

¿Por qué el compilador elige la versión que toma una A cuando B es la clase más derivada?

Como otros han notado, el compilador lo hace porque eso es lo que la especificación del lenguaje dice que haga.

Esta podría ser una respuesta insatisfactoria. Un seguimiento natural sería "¿qué principios de diseño subyacen a la decisión de especificar el idioma de esa manera?"

Esa es una pregunta frecuente, tanto en StackOverflow como en mi buzón. La respuesta breve es "este diseño mitiga la familia de errores de la clase base frágil".

Para obtener una descripción de la función y por qué está diseñada tal como está, consulte mi artículo sobre el tema:

http://blogs.msdn.com/b/ericlippert/archive/2007/09/04/future-breaking-changes-part-three.aspx

Para más artículos sobre el tema de cómo varios idiomas tratan el problema de la clase base frágil, consulte mi archivo de artículos sobre el tema:

http://blogs.msdn.com/b/ericlippert/archive/tags/brittle+base+classes/

Aquí está mi respuesta a la misma pregunta de la semana pasada, que se parece notablemente a esta.

¿Por qué se ignoran las firmas declaradas en la clase base?

Y aquí hay tres preguntas más relevantes o duplicadas:

¿Resolución de sobrecarga de C #?

Método de resolución de sobrecargas y Rompecabezas de Jon Skeet

¿Por qué funciona esto? Método de sobrecarga + método anulación + polimorfismo

Considere el siguiente código (es un poco largo, pero espero que pueda seguirlo):

class A { } class B : A { } class C { public virtual void Foo(B b) { Console.WriteLine("base.Foo(B)"); } } class D: C { public override void Foo(B b) { Console.WriteLine("Foo(B)"); } public void Foo(A a) { Console.WriteLine("Foo(A)"); } } class Program { public static void Main() { B b = new B(); D d = new D (); d.Foo(b); } }

Si crees que el resultado de este programa es "Foo (B)", estarás en el mismo barco que yo: ¡completamente equivocado! De hecho, produce "Foo (A)"

Si elimino el método virtual de la clase C , funciona como se esperaba: "Foo (B)" es la salida.

¿Por qué el compilador elige la versión que toma una A cuando B es la clase más derivada?


Creo que al implementar otra clase se ve tan lejos en el árbol para obtener una implementación sólida de un método. Como no se llama ningún método, usa la clase base.

public void Foo(A a){ Console.WriteLine("Foo(A)" + a.GetType().Name); Console.WriteLine("Foo(A)" +a.GetType().BaseType ); }

eso es una suposición de que no soy un profesional en .Net


Creo que es porque en el caso de un método no virtual, se utiliza el tipo de tiempo de compilación de la variable en la que se invoca el método.

Usted tiene el método Foo que no es virtual y, por lo tanto, se llama ese método.

Este enlace tiene una explicación muy buena http://msdn.microsoft.com/en-us/library/aa645767%28VS.71%29.aspx


Entonces, aquí es cómo debería funcionar de acuerdo con la especificación (en tiempo de compilación, y dado que navegué los documentos correctamente):

El compilador identifica una lista de métodos de coincidencia del tipo D y sus tipos base, en función del nombre del método y la lista de argumentos. Esto significa que cualquier método llamado Foo , tomando un parámetro de un tipo para el cual hay una conversión implícita de B son candidatos válidos. Eso produciría la siguiente lista:

C.Foo(B) (public virtual) D.Foo(B) (public override) D.Foo(A) (public)

De esta lista, se excluyen las declaraciones que incluyen un modificador de anulación. Eso significa que la lista ahora contiene los siguientes métodos:

C.Foo(B) (public virtual) D.Foo(A) (public)

En este momento tenemos la lista de candidatos coincidentes, y el compilador ahora debe decidir a qué llamar. En el documento 7.5.5.1 Invocaciones de método , encontramos el siguiente texto:

Si N es aplicable con respecto a A ( Sección 7.4.2.1 ), entonces todos los métodos declarados en un tipo base de T se eliminan del conjunto.

Esto significa esencialmente que si hay un método aplicable declarado en D , cualquier método de las clases base se eliminará de la lista. En este punto tenemos un ganador:

D.Foo(A) (public)


La respuesta está en la especificación C # sección 7.3 y la sección 7.5.5.1

Desglose los pasos utilizados para elegir el método a invocar.

  • Primero, se construye el conjunto de todos los miembros accesibles denominados N ( N=Foo ) declarados en T ( T=class D ) y los tipos base de T ( class C ). Las declaraciones que incluyen un modificador de anulación se excluyen del conjunto ( D.Foo (B) se excluye )

    S = { C.Foo(B) ; D.Foo(A) }

  • Se construye el conjunto de métodos candidatos para la invocación del método. Comenzando con el conjunto de métodos asociados con M, que se encontraron mediante la búsqueda de miembro anterior, el conjunto se reduce a los métodos que son aplicables con respecto a la lista de argumentos AL ( AL=B ). La reducción establecida consiste en aplicar las siguientes reglas a cada método TN en el conjunto, donde T ( T=class D ) es el tipo en el que se declara el método N ( N=Foo ):

    • Si N no es aplicable con respecto a AL ( Sección 7.4.2.1 ), entonces N se elimina del conjunto.

      • C.Foo(B) es aplicable con respecto a AL
      • D.Foo(A) es aplicable con respecto a AL

        S = { C.Foo(B) ; D.Foo(A) }

    • Si N es aplicable con respecto a AL (Sección 7.4.2.1), entonces todos los métodos declarados en un tipo de base de T se eliminan del conjunto . C.Foo(B) se elimina del conjunto

      S = { D.Foo(A) }

Al final, el ganador es D.Foo(A) .

Si el método abstracto se elimina de C

Si el método abstracto se elimina de C, el conjunto inicial es S = { D.Foo(B) ; D.Foo(A) } S = { D.Foo(B) ; D.Foo(A) } y la regla de resolución de sobrecarga debe usarse para seleccionar el mejor miembro de función en ese conjunto.

En este caso, el ganador es D.Foo(B) .