method - virtual c#
¿Por qué no hacer todo ''virtual''? (6)
En muchos casos, es crucial que una clase funcione correctamente para que un método dado tenga un comportamiento específico. Si el método se reemplaza en una clase heredada, no hay garantía de que el método implementará correctamente el comportamiento esperado. Solo debe marcar un método virtual si su clase está diseñada específicamente para herencia y admitirá un método con una implementación diferente. Diseñar para la herencia no es fácil, hay muchos casos en los que anular incorrectamente un método romperá el comportamiento interno de la clase
Posible duplicado:
¿Por qué C # implementa métodos como no virtuales de forma predeterminada?
Estoy hablando principalmente de C #, .NET 3.5, pero me pregunto en general cuáles son los beneficios de no considerar todo "virtual", es decir, un método llamado en una instancia de una clase secundaria siempre ejecuta la versión más infantil. de ese método. En C #, este no es el caso si el método principal no está etiquetado con el modificador "virtual". Ejemplo:
public class Parent
{
public void NonVirtual() { Console.WriteLine("Non-Virtual Parent"); }
public virtual void Virtual(){ Console.WriteLine("Virtual Parent"); }
}
public class Child : Parent
{
public new void NonVirtual() { Console.WriteLine("Non-Virtual Child"); }
public override void Virtual() { Console.WriteLine("Virtual Child"); }
}
public class Program
{
public static void Main(string[] args)
{
Child child = new Child();
Parent parent = new Child();
var anon = new Child();
child.NonVirtual(); // => Child
parent.NonVirtual(); // => Parent
anon.NonVirtual(); // => Child
((Parent)child).NonVirtual(); // => Parent
child.Virtual(); // => Child
parent.Virtual(); // => Child
anon.Virtual(); // => Child
((Parent)child).Virtual(); // => Child
}
}
¿Cuáles son exactamente los beneficios del comportamiento no virtual observado anteriormente? Lo único en lo que podía pensar era "¿Qué pasa si el autor de Parent no quiere que su método sea virtual?" pero luego me di cuenta de que no podía pensar en un buen caso de uso para eso. Se podría argumentar que el comportamiento de la clase depende de cómo funciona un método no virtual, pero me parece que hay una encapsulación deficiente o que el método debería estar sellado.
En esta misma línea, parece que "esconderse" es normalmente una mala idea. Después de todo, si se crearon un objeto y métodos secundarios, parece que se hizo por una razón específica para anular al principal. Y si Child implementa (y oculta a los padres) NonVirtual (), es muy fácil no obtener lo que muchos podrían considerar el comportamiento "esperado" de llamar a Child :: NonVirtual (). (Digo "esperado" porque a veces es fácil no darse cuenta de que ''esconderse'' está ocurriendo).
Entonces, ¿cuáles son los beneficios de no permitir que todo tenga un comportamiento "virtual"? ¿Cuál es un buen caso de uso para ocultar un padre no virtual si es tan fácil obtener un comportamiento inesperado?
Si alguien tiene curiosidad por saber por qué hago esta pregunta, hace poco estuve examinando la biblioteca DynamicProxy de Castle Projects. El único obstáculo principal para usarlo es que cualquier método (o propiedad) que desee usar como proxy debe ser virtual. Y esto no siempre es una opción para los desarrolladores (si no tenemos control sobre la fuente). Sin mencionar que el propósito de DynamicProxy es evitar el acoplamiento entre su clase de proxy y cualquier comportamiento que esté tratando de lograr con el proxy (como el registro o tal vez una implementación de Memoization). Y al forzar a los métodos virtuales a lograr esto, lo que se logra es un acoplamiento muy delgado pero obtuso de DynamicProxy a todas las clases que está procesando. Imagínese, tiene un montón de métodos etiquetados como virtuales, aunque nunca se heredan ni se anulan, por lo que cualquier otro El desarrollador que mira el código podría preguntarse "¿por qué son incluso virtuales? vamos a cambiarlos de nuevo".
De todos modos, la frustración me llevó a preguntarme cuáles son los beneficios de lo no virtual, cuando parece que todo lo virtual podría haber sido más claro (OMI, supongo) y quizás (?) Tiene más beneficios.
EDITAR: etiquetar como wiki de la comunidad, ya que parece una pregunta que podría tener respuestas subjetivas
En un marco, se podría llamar a un miembro no virtual y tener un rango de resultados esperados, si el método fuera virtual, el resultado del método podría ser un resultado esperado que no se probó. Permitir que los métodos no sean virtuales da los resultados esperados a las acciones del marco.
Porque no quieres que la gente anule métodos para los que no has diseñado la clase. Se requiere un gran esfuerzo para asegurarse de que es seguro anular un método o incluso derivar de una clase. Es mucho más seguro hacerlo no virtual
si no ha considerado lo que podría suceder.
Simple: el punto entero en una clase es encapsular algún tipo de abstracción. Por ejemplo, queremos un objeto que se comporte como una cadena de texto.
Ahora, si todo hubiera sido virtual, podría hacer esto:
class MessedUpString : String{
override void Trim() { throw new Exception(); }
}
y luego pasar esto a alguna función que espera una cadena. Y en el momento en que tratan de cortar esa cuerda, explota.
La cadena ya no se comporta como una cadena. ¿Cómo es eso algo bueno?
Si todo se hace virtual, tendrá dificultades para hacer cumplir los invariantes de clase. Permites que la abstracción de la clase se rompa.
De forma predeterminada, una clase debe encapsular las reglas y los comportamientos que se espera que sigan. Todo lo que haces virtual es, en principio, un gancho de extensibilidad, la función se puede cambiar para hacer cualquier cosa . Eso solo tiene sentido en algunos casos, cuando tenemos un comportamiento que en realidad es definido por el usuario.
La razón por la que las clases son útiles es que nos permiten ignorar los detalles de la implementación. Simplemente podemos decir "este es un objeto de cadena, sé que se va a comportar como una cadena. Sé que nunca violará ninguna de estas garantías". Si esa garantía no se puede mantener, la clase es inútil. También puede hacer públicos todos los miembros de datos y mover los métodos de miembros fuera de la clase.
¿Conoces el principio de sustitución de Liskov ? En cualquier lugar donde se espere un objeto de la clase B de base, debería poder pasar un objeto de la clase D derivada. Esa es una de las reglas más fundamentales de la programación orientada a objetos. Necesitamos saber que las clases derivadas seguirán funcionando cuando las pasemos a la clase base y las pasemos a una función que espera a la clase base. Eso significa que tenemos que hacer que algún comportamiento sea fijo e inmutable.
Un beneficio clave de un método no virtual es que se puede enlazar en tiempo de compilación. Es decir, el compilador puede estar seguro de qué método real debe llamarse cuando se utiliza un método en el código.
El método real a ser llamado no se puede conocer en tiempo de compilación si ese método se declara virtual, ya que la referencia puede apuntar realmente a un subtipo que lo ha reemplazado. Por lo tanto, hay una pequeña sobrecarga en tiempo de ejecución cuando el método real para llamar necesita ser resuelto.