programacion lenguaje instrucciones ejemplos bloque basicas c# interface code-snippets

lenguaje - ¿Por qué este código de C#devuelve lo que hace



instrucciones en programacion ejemplos (3)

¿Puede alguien ayudarme a entender por qué este fragmento de código devuelve "Bar-Bar-Quux"? Me está costando entender esto incluso después de leer en las interfaces.

interface IFoo { string GetName(); } class Bar : IFoo { public string GetName() { return "Bar"; } } class Baz : Bar { public new string GetName() { return "Baz"; } } class Quux : Bar, IFoo { public new string GetName() { return "Quux"; } } class Program { static void Main() { Bar f1 = new Baz(); IFoo f2 = new Baz(); IFoo f3 = new Quux(); Console.WriteLine(f1.GetName() + "-" + f2.GetName() + "-" + f3.GetName()); } }


Hay dos cosas pasando aquí. Uno es miembro de la clandestinidad. Esto es bastante conocido y cubierto en elsewhere . La otra característica menos conocida es la reimplementación de la interfaz cubierta en la sección 13.4.6 de la especificación C # 5. Citar:

A una clase que hereda una implementación de interfaz se le permite volver a implementar la interfaz incluyéndola en la lista de clases base. Una reimplementación de una interfaz sigue exactamente las mismas reglas de mapeo de interfaz que una implementación inicial de una interfaz. Por lo tanto, la asignación de interfaz heredada no tiene ningún efecto en la asignación de interfaz establecida para la reimplementación de la interfaz.

y

Las declaraciones de miembro público heredado y las declaraciones de miembros de interfaz explícita heredadas participan en el proceso de asignación de interfaz para interfaces reimplementadas.

El resultado para f1.GetName() es "Bar" porque el método Baz.GetName está ocultando Bar.GetName y f1 se declara como tipo Bar . No hay despacho a la implementación del tipo de tiempo de ejecución a menos que se declare explícitamente como virtual y se invalide.

Del mismo modo, para f2.GetName() , Baz.GetName está ocultando la implementación en Bar , por lo que no se llama cuando se utiliza el envío a través de una referencia a la interfaz. La interfaz está "asignada" al método declarado en Bar porque ese es el tipo en el que se declaró la interfaz. No importa que Baz tenga un método compatible con el mismo nombre. Las reglas para el mapeo de interfaz se definen en la sección 13.4.4 de la especificación. Si GetName hubiera declarado virtual en Bar , podría anularse, y luego se llamaría a través de la interfaz. El resultado es por lo tanto también "Bar".

Para f3.GetName() , Quux vuelve a implementar IFoo para que pueda definir su propia asignación a GetName . Tenga en cuenta que también oculta la implementación heredada de Bar . No es necesario usar nuevo para hacer la reimplementación, simplemente suprime la advertencia sobre la ocultación. Por lo tanto, el resultado es "Quux".

Entonces eso explica el resultado que ves: "Bar-Bar-Quux"

Esta post de Eric Lippert analiza algunos matices más en esta complicada función.


La salida es Bar-Bar-Quux como resultado de 3 llamadas a GetName () en su llamada al método Console.WriteLine.

Bar f1 = new Baz(); IFoo f2 = new Baz(); IFoo f3 = new Quux(); Console.WriteLine(f1.GetName() + "-" + f2.GetName() + "-" + f3.GetName()); //Bar-Bar-Quux

Examinemos cada llamada para que quede más claro lo que sucede.

f1.GetName ()

f1 se instancia como Baz . Sin embargo , está escrito como Bar . Como Bar expone GetName , cuando se f1.GetName() , ese es el método que se llama, independientemente del hecho de que Baz también implemente GetName . La razón es que f1 no está escrito como Baz , y si lo fuera, llamaría GetName método GetName Baz . Un ejemplo de eso sería examinar el resultado de

Console.WriteLine(((Baz)f1).GetName() + "-" + f2.GetName() + "-" + f3.GetName()); //Baz-Bar-Quux

Esto es posible debido a dos hechos. Primero, f1 se instanciaba inicialmente como Baz , simplemente se tipeaba como Bar . En segundo lugar, Baz tiene un método GetName , y el uso de new en su definición oculta el método GetName heredado de la Bar permitiendo que se GetName a GetName Baz .

f2.GetName ()

Se está produciendo una tipificación muy similar con f2 , que se define como

IFoo f2 = new Baz();

Si bien la clase Baz implementa un método GetName , no implementa el método GetName porque Baz no hereda de IFoo y, por lo tanto, el método no está disponible. Bar implementa IFoo , y como Baz hereda de Bar , GetName Bar es el método que se expone cuando f2 se escribe como IFoo .

Nuevamente, dado que f2 inicialmente se instanciaba como Baz , aún se puede convertir a Baz .

Console.WriteLine(f1.GetName() + "-" + ((Baz)f2).GetName() + "-" + f3.GetName()); //Bar-Baz-Quux

Y tendrá el mismo resultado de salida por el motivo indicado anteriormente para f1 ( f2 se tipeó originalmente como Baz , y el método GetName Baz oculta el método GetName heredado de la Bar ).

f3.GetName ()

Otra historia aquí. Quux hereda e implementa IFoo , y oculta la implementación de IFoo de IFoo mediante el uso de new . El resultado es que el método GetName Quux es el que se llama.


Las interfaces, por definición, no tienen una implementación asociada, lo que quiere decir que sus métodos son siempre virtuales y abstractos. Por el contrario, la Bar clase anterior define una implementación concreta para GetName . Esto satisface el contrato requerido para implementar IFoo .

Class Baz ahora hereda de Bar y declara un new método GetName . Es decir, la clase padre Bar tiene un método con el mismo nombre, pero se ignora por completo cuando se trabaja con objetos Baz explícitamente.

Sin embargo, si un objeto Baz se lanza como una Bar , o simplemente se asigna a una variable de tipo Bar o IFoo , hará lo que le digan y se comportará como una Bar . En otras palabras, el nombre de método GetName refiere a Bar.GetName lugar de Baz.GetName .

Ahora, en el tercer caso, Quux hereda de Bar e implementa IFoo . Ahora, cuando se presenta como IFoo , proporcionará su propia implementación (de acuerdo con la especificación proporcionada en la respuesta de Mike Z).

Sin embargo, cuando un Quux se lanza como una barra, devuelve "Barra", al igual que Baz.