Métodos no virtuales, enlace estático e interfaz en C #

Entiendo que los métodos no virtuales son inactivamente enlazado, lo que significa, por lo que tengo entendido, que se sabe en tiempo de compilación en cuanto a qué método se invocará en qué objeto. Esta decisión se toma en base a la estático tipo de objeto (s). Lo que me confunde es las interfaces (más bien que clase) y enlace estático.

Considere este código,

public interface IA
{
    void f();
}
public class A : IA
{
    public void f() {  Console.WriteLine("A.f()"); }
}
public class B : A 
{
    public new void f() {  Console.WriteLine("B.f()"); }
}

B b = new B();
b.f();  //calls B.f()     //Line 1

IA ia = b as IA;
ia.f(); //calls A.f()     //Line 2

Código de demostración: http://ideone.com/JOVmi

Yo entiendo el Line 1. El compilador puede saber que b.f() invocará B.f() porque conoce el estático tipo de b cual es B.

Pero, ¿cómo decide el compilador en tiempo de compilación mismo que ia.f() llamará A.f()? Cuál es el estático tipo de objeto ia? No lo es IA? Pero eso es una interfaz y no tiene ninguna definición de f(). Entonces, ¿cómo es que funciona?

Para hacer el caso más desconcertante, consideremos esto static método:

static void g(IA ia)
{
   ia.f(); //What will it call? There can be too many classes implementing IA!
}

Como dice el comentario, puede haber demasiadas clases que implementan la interfaz IA, entonces, ¿cómo se puede compilar inactivamente decidir que método ia.f() ¿Llamaría? Quiero decir, digamos si tengo una clase definida como:

public class C : A, IA 
{
    public new void f() { Console.WriteLine("C.f()"); }
}

Como ves, C, diferente a B, implementos IA además de derivar de A. Eso significa que tenemos un comportamiento diferente aquí:

g(new B()); //inside g(): ia.f() calls A.f() as before!
g(new C()); //inside g(): ia.f() doesn't calls A.f(), rather it calls C.f()

Código de demostración: http://ideone.com/awCor

¿Cómo entendería todas estas variaciones, especialmente cómo funcionan las interfaces y el enlace estático?

Y pocos más (ideone):

C c = new C();
c.f(); //calls C.f()

IA ia = c as IA;
ia.f(); //calls C.f()

A a = c as A;
a.f(); //doesn't call C.f() - instead calls A.f()

IA iaa = a as IA;
iaa.f(); //calls C.f() - not A.f()

Ayúdame a comprender todo esto y cómo el compilador de C # realiza el enlace estático.

preguntado el 27 de agosto de 11 a las 18:08

2 Respuestas

Pero, ¿cómo decide el compilador en tiempo de compilación mismo que ia.f() llamará A.f()?

No es así. Sabe que ia.f() estaré llamando IA.f() en la instancia de objeto contenida en ia. Emite este código de operación de llamada y permite que el tiempo de ejecución lo averigüe cuándo se ejecuta la llamada.

Aquí está el IL que se emitirá para la mitad inferior de su código de ejemplo:

    .locals init (
            class B   V_0,
            class IA  V_1)
    IL_0000:  newobj instance void class B::'.ctor'()
    IL_0005:  stloc.0
    IL_0006:  ldloc.0
    IL_0007:  callvirt instance void class B::f()
    IL_000c:  ldloc.0
    IL_000d:  stloc.1
    IL_000e:  ldloc.1
    IL_000f:  callvirt instance void class IA::f()
    IL_0014:  ret

Tenga en cuenta que callvirt se utiliza en ambos casos. Esto se usa porque el tiempo de ejecución puede determinar por sí mismo cuándo el método de destino no es virtual. (Adicionalmente, callvirt realiza una verificación nula implícita en el this argumento, mientras call no es.)

Este volcado de IL debería responder a todas sus otras preguntas. En resumen: el compilador ni siquiera intenta resolver la llamada final al método. Eso es un trabajo para el tiempo de ejecución.

Respondido 27 ago 11, 22:08

¿Qué enlace estático ¿Quiere decir en C # entonces? - Nawaz

Significa que la llamada se resuelve en tiempo de compilación a un método específico (ya sea un método de interfaz o no) en lugar de simplemente emitir el método nombre y dejando que el tiempo de ejecución se calcule todo fuera. En este caso, la llamada está vinculada estáticamente a un específico método de interfaz. El compilador no puede esperar saber qué tipo de objeto invocará en el escenario de la interfaz. Lo mismo se aplica a los métodos virtuales. El único escenario en el que el compilador enlatado Determinar que la llamada final al método es cuando se invoca un método no virtual en una variable de tipo que no es de interfaz (clase o estructura). - cdhowie

El enlace estático significa algo más de lo que cree. También llamado 'enlace temprano', es lo opuesto al enlace tardío, disponible en C # versión 4 con el lugar de trabajo dinámico palabra clave y en todas las versiones con reflejo. La característica principal del enlace tardío es que el compilador no puede verificar que el método llamado exista, y mucho menos verificar que se pasen los argumentos adecuados. Si algo falla, obtendrá una excepción de tiempo de ejecución. También es lento porque el tiempo de ejecución necesita hacer un trabajo adicional para buscar el método, verificar los argumentos y construir el marco de la pila de llamadas.

Esto no es un problema cuando usa interfaces o métodos virtuales, el compilador puede verificar todo por adelantado. El código resultante es eficiente. Esto todavía da como resultado llamadas a métodos indirectos (también conocido como 'envío dinámico'), necesarias para implementar interfaces y métodos virtuales, pero que también se utilizan en C # para métodos de instancia no virtuales. Documentado en este blog de un ex miembro del equipo de C #. La plomería CLR que hace que esto funcione se llama 'tabla de métodos'. Aproximadamente análoga a una tabla v en C ++, pero la tabla de métodos contiene una entrada para cada método, incluidos los no virtuales. Una referencia de interfaz es simplemente un puntero en esta tabla.

Respondido 28 ago 11, 00:08

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.