Pregunta

Los siguientes programa imprime

A:C(A,B)
B:C(A,B)

(como debería)

public interface I
{
    string A();
}

public class C : I
{
    public string A()
    {
        return "A";
    }

    public string B()
    {
        return "B";
    }
}

public class A
{
    public virtual void Print(C c)
    {
        Console.WriteLine("A:C(" + c.A() + "," + c.B() + ")");
    }
}

public class B : A
{
    public new void Print(C c)
    {
        Console.WriteLine("B:C(" + c.A() + "," + c.B() + ")");
    }

    public void Print(I i)
    {
        Console.WriteLine("B:I(" + i.A() + ")");
    }
}

class Program
{
    public static void Main(string[] args)
    {
        A a = new A();
        B b = new B();
        C c = new C();
        a.Print(c);
        b.Print(c);
    }
}

Sin embargo, si cambio de palabras clave 'nuevo' a 'anulación' en la clase B, así:

    public override void Print(C c)

todo de un programa de repente comienza a imprimir:

A:C(A,B)
B:I(A)

¿Por qué?

¿Fue útil?

Solución

Esto tiene que ver con la forma en métodos sobrecargados son resueltos.

Efectivamente (simplificado un poco), el compilador busca primero en el tipo declarado de la expresión (B) en este caso y busca métodos candidatos que se declaró por primera vez en ese tipo . Si hay algún métodos que sean apropiados (es decir, cuando todos los argumentos pueden ser convertidos a los tipos de parámetros del método), entonces no vistazo a cualquier tipo de padres. Esto significa que los métodos sustituidos, en los que la declaración inicial está en un tipo de matriz, no consiguen un puesto de observación en si hay algún "recién declarados" métodos apropiados en el tipo derivado.

Aquí hay un ejemplo un poco más simple:

using System;

class Base
{
    public virtual void Foo(int x)
    {
        Console.WriteLine("Base.Foo(int)");
    }
}

class Derived : Base
{
    public override void Foo(int x)
    {
        Console.WriteLine("Derived.Foo(int)");
    }

    public void Foo(double d)
    {
        Console.WriteLine("Derived.Foo(double)");
    }
}

class Test
{
    static void Main()
    {
        Derived d = new Derived();
        d.Foo(10);
    }
}

Esto imprime Derived.Foo(double) - a pesar de que el compilador sabe que hay un método de correspondencia con un parámetro de tipo int, y el argumento es de tipo int, y la conversión de int a int es "mejor" que la conversión de int a double, el hecho de que sólo el método Foo(double) es originalmente declarado en Derived significa que el compilador ignora Foo(int).

Esto es muy sorprendente la OMI. Puedo ver por qué sería el caso si Derived no anula Foo - la introducción de otra manera un nuevo método, más específica en la clase base podría cambiar el comportamiento inesperado - pero claramente Derived aquí sabe sobre Base.Foo(int) ya que está anulando la misma. Este es uno de los (relativamente pocos) puntos donde creo que los diseñadores de C # tomado la decisión equivocada.

Otros consejos

Ok, así

    public new void Print(C c)
    {
        Console.WriteLine("B:C(" + c.A() + "," + c.B() + ")");
    }

    public void Print(I i)
    {
        Console.WriteLine("B:I(" + i.A() + ")");
    }

Esto declara un nuevo método para la impresión. Ahora, debido B hereda de A, que está llamando simly el nuevo método en dos ocasiones. Cuando overide el método, esto luego cambia la firma del método cuando se llama a A, pero cuando se llama a la firma B, entonces tiene su propio método de firma.

No estoy seguro de si me estoy explicando pregunta clara, pero bueno.

utilizando nueva:

A y B reciben la misma aplicación del método de impresión.

usando override:

A tiene un método de firma diferente a B lo más, que no han cambiado en la firma del método B sólo en A.

utilizando el nuevo básicamente se hace caso omiso de esto:

    public void Print(I i)
    {
        Console.WriteLine("B:I(" + i.A() + ")");
    }

Esta fue una gran pregunta.
Todas las respuestas se pueden encontrar aquí: http://msdn.microsoft.com/en-us /library/6fawty39(VS.80).aspx

El quid de la cuestión es la siguiente:

  

... el compilador de C # en primer lugar tratar de hacer   la llamada compatible con las versiones   de [functionName] declarada originalmente en   [La clase derivada]. métodos de anulación no son   considerado como declaró en una clase,   que son nuevas implementaciones de una   método declarado en una clase base. Solamente   si el compilador de C # no puede coincidir con el   método de llamada a un método original de   [La clase derivada] va a tratar de igualar la llamada   a un método reemplazado con el mismo   nombre y parámetros compatibles.

Así que porque tiene un nuevo método de impresión (Ii) en la clase derivada que coincide con el argumento de "C", (porque c implementa I), ese método tiene prioridad sobre el método de "anular".

Cuando se marca el método como "nuevo", que se consideran tanto para ser implementado en la clase derivada, y la (c C) Método de impresión es más compatible con el parámetro "c", por lo que tiene prioridad.

Esto es al menos lo mismo una pregunta acerca de cómo el método sobrecarga funciona en C #. Creo que haya resaltado una situación interesante aquí ...

En el primer caso (usando la palabra clave new en el método), el compilador decide utilizar la sobrecarga del método Print con el parámetro de tipo C porque es tipo es exactamente equivalente a la del parámetro pasado (es decir, sin conversión implícita es requerido), mientras que una conversión implícita a la interfaz que se requeriría si el compilador eran para elegir el método Print que toma un argumento de tipo I -. en otras palabras, se elige el más "evidente" sobrecarga del método

En el segundo caso (usando la palabra clave override en el método), el compilador decide utilizar la sobrecarga de Print con el parámetro de tipo I ya que si bien está sustituyendo la sobrecarga del método Print(C c) en la clase B, se define de manera efectiva en a la clase padre, haciendo que la sobrecarga del método Print(I i) de hecho, la sobrecarga de más alto nivel y por lo tanto la más directa, es decir, la primera de ellas se encuentra el compilador.

Esperamos que esto ayudará a comprender. Déjame saber si necesito clairfy los puntos más ...

Nota:. Si me equivoco en decir que el compilador hace estas cosas, entonces por favor, corríjanme, aunque no hay mucha diferencia por el bien del argumento si es el compilador o CLR / JIT, parecería

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top