¿Hay alguna forma de anular un método no virtual? o algo que dé resultados similares (además de crear un nuevo método para llamar al método deseado)?
Me gustaría anular un método Microsoft.Xna.Framework.Graphics.GraphicsDevice
teniendo en cuenta las pruebas unitarias.
c#
overriding
overloading
zfedoran
fuente
fuente
Respuestas:
No, no puede anular un método no virtual. Lo más cercano que puede hacer es ocultar el método creando un
new
método con el mismo nombre, pero esto no es recomendable ya que rompe los buenos principios de diseño.Pero incluso ocultar un método no le dará tiempo de ejecución de envío polimórfico de llamadas de método como lo haría una verdadera llamada de método virtual. Considere este ejemplo:
using System; class Example { static void Main() { Foo f = new Foo(); f.M(); Foo b = new Bar(); b.M(); } } class Foo { public void M() { Console.WriteLine("Foo.M"); } } class Bar : Foo { public new void M() { Console.WriteLine("Bar.M"); } }
En este ejemplo, ambas llamadas al
M
método printFoo.M
. Como se puede ver este enfoque permitirá tener una nueva implementación de un método siempre que la referencia a ese objeto es del tipo derivado correcta, pero que oculta un método de base hace polimorfismo descanso.Le recomendaría que no oculte los métodos base de esta manera.
Tiendo a ponerme del lado de aquellos que favorecen el comportamiento predeterminado de C # de que los métodos no son virtuales por defecto (a diferencia de Java). Yo iría aún más lejos y diría que las clases también deberían estar selladas por defecto. La herencia es difícil de diseñar correctamente y el hecho de que haya un método que no esté marcado como virtual indica que el autor de ese método nunca tuvo la intención de que el método fuera anulado.
Editar: "envío polimórfico en tiempo de ejecución" :
Lo que quiero decir con esto es el comportamiento predeterminado que ocurre en el momento de la ejecución cuando llama a métodos virtuales. Digamos, por ejemplo, que en mi ejemplo de código anterior, en lugar de definir un método no virtual, de hecho definí un método virtual y un verdadero método anulado también.
Si
b.Foo
tuviera que llamar en ese caso, el CLR determinaría correctamente el tipo de objeto al queb
apunta la referenciaBar
y enviaría la llamada de maneraM
apropiada.fuente
No, no puedes.
Solo puede anular un método virtual; consulte MSDN aquí :
fuente
Si la clase base no está sellada, puede heredarla y escribir un nuevo método que oculte el base (use la palabra clave "nuevo" en la declaración del método). De lo contrario, no, no puede anularlo porque nunca fue la intención del autor original que se anulara, por lo que no es virtual.
fuente
Creo que se está sobrecargando y anulando confuso, sobrecargar significa que tiene dos o más métodos con el mismo nombre pero diferentes conjuntos de parámetros, mientras que anular significa que tiene una implementación diferente para un método en una clase derivada (reemplazando o modificando así el comportamiento en su clase base).
Si un método es virtual, puede anularlo mediante la palabra clave anular en la clase derivada. Sin embargo, los métodos no virtuales solo pueden ocultar la implementación base utilizando la nueva palabra clave en lugar de la palabra clave anular. La ruta no virtual es inútil si la persona que llama accede al método a través de una variable escrita como tipo base, ya que el compilador usaría un envío estático al método base (lo que significa que nunca se llamaría al código en su clase derivada).
Nunca hay nada que le impida agregar una sobrecarga a una clase existente, pero solo el código que conoce su clase podría acceder a ella.
fuente
No puede anular el método no virtual de ninguna clase en C # (sin piratear CLR), pero puede anular cualquier método de interfaz que implemente la clase. Considere que no hemos sellado
class GraphicsDevice: IGraphicsDevice { public void DoWork() { Console.WriteLine("GraphicsDevice.DoWork()"); } } // with its interface interface IGraphicsDevice { void DoWork(); } // You can't just override DoWork in a child class, // but if you replace usage of GraphicsDevice to IGraphicsDevice, // then you can override this method (and, actually, the whole interface). class MyDevice: GraphicsDevice, IGraphicsDevice { public new void DoWork() { Console.WriteLine("MyDevice.DoWork()"); base.DoWork(); } }
Y aquí está la demostración
class Program { static void Main(string[] args) { IGraphicsDevice real = new GraphicsDevice(); var myObj = new MyDevice(); // demo that interface override works GraphicsDevice myCastedToBase = myObj; IGraphicsDevice my = myCastedToBase; // obvious Console.WriteLine("Using real GraphicsDevice:"); real.DoWork(); // override Console.WriteLine("Using overriden GraphicsDevice:"); my.DoWork(); } }
fuente
En el caso de que esté heredando de una clase no derivada, simplemente podría crear una superclase abstracta y heredar de ella en su lugar.
fuente
No puede anular un método no virtual. Sin embargo, puede utilizar la
new
palabra clave modificadora para obtener resultados similares:class Class0 { public int Test() { return 0; } } class Class1 : Class0 { public new int Test() { return 1; } } . . . // result of 1 Console.WriteLine(new Class1().Test());
También querrá asegurarse de que el modificador de acceso también sea el mismo; de lo contrario, no obtendrá herencia en el futuro. Si otra clase hereda de
Class1
lanew
palabra claveClass1
, no afectará a los objetos que hereden de ella, a menos que el modificador de acceso sea el mismo.Si el modificador de acceso no es el mismo:
class Class0 { protected int Test() { return 0; } } class Class1 : Class0 { // different access modifier new int Test() { return 1; } } class Class2 : Class1 { public int Result() { return Test(); } } . . . // result of 0 Console.WriteLine(new Class2().Result());
... versus si el modificador de acceso es el mismo:
class Class0 { protected int Test() { return 0; } } class Class1 : Class0 { // same access modifier protected new int Test() { return 1; } } class Class2 : Class1 { public int Result() { return Test(); } } . . . // result of 1 Console.WriteLine(new Class2().Result());
Como se señaló en una respuesta anterior, este no es un buen principio de diseño.
fuente
Hay una forma de lograr esto usando la clase abstracta y el método abstracto.
Considerar
Class Base { void MethodToBeTested() { ... } void Method1() { } void Method2() { } ... }
Ahora, si desea tener diferentes versiones del método MethodToBeTested (), cambie Class Base a una clase abstracta y el método MethodToBeTested () como método abstracto
abstract Class Base { abstract void MethodToBeTested(); void Method1() { } void Method2() { } ... }
Con MethodToBeTested () vacío abstracto viene un problema; la implementación se ha ido.
Por lo tanto, cree un
class DefaultBaseImplementation : Base
para tener la implementación predeterminada.Y cree otro
class UnitTestImplementation : Base
para implementar la prueba unitaria.Con estas 2 nuevas clases, se puede anular la funcionalidad de la clase base.
Class DefaultBaseImplementation : Base { override void MethodToBeTested() { //Base (default) implementation goes here } } Class UnitTestImplementation : Base { override void MethodToBeTested() { //Unit test implementation goes here } }
Ahora tiene 2 clases implementadas (anuladas)
MethodToBeTested()
.Puede crear una instancia de la clase (derivada) según sea necesario (es decir, con la implementación base o con la implementación de la prueba unitaria).
fuente