Cómo escondite método funciona en C #? (La segunda parte)
-
22-08-2019 - |
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é?
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