A diferencia de Java, ¿por qué C # trata los métodos como funciones no virtuales de forma predeterminada? ¿Es más probable que sea un problema de desempeño que otros posibles resultados?
Recuerdo haber leído un párrafo de Anders Hejlsberg sobre varias ventajas que está aportando la arquitectura existente. Pero, ¿qué pasa con los efectos secundarios? ¿Es realmente una buena compensación tener métodos no virtuales por defecto?
c#
java
.net
virtual-functions
Burcu Dogan
fuente
fuente
this
referencia es nula. Consulte aquí para obtener más información.Respuestas:
Las clases deben estar diseñadas para la herencia para poder aprovecharla. Tener métodos
virtual
por defecto significa que todas las funciones de la clase se pueden desconectar y reemplazar por otras, lo cual no es realmente bueno. Mucha gente incluso cree que las clases deberían haber sidosealed
por defecto.virtual
Los métodos también pueden tener una ligera implicación en el rendimiento. Sin embargo, no es probable que esta sea la razón principal.fuente
callvirt
por un simplecall
en el código IL (o incluso más adelante cuando se hace JIT). Java HotSpot hace lo mismo. El resto de la respuesta es acertada.Me sorprende que parezca haber tal consenso aquí de que no virtual por defecto es la forma correcta de hacer las cosas. Voy a caer en el otro lado, creo que pragmático, de la valla.
La mayoría de las justificaciones me parecen el viejo argumento de "Si te damos el poder, podrías lastimarte". ¿De los programadores?
Me parece que el codificador que no sabía lo suficiente (o no tenía suficiente tiempo) para diseñar su biblioteca para herencia y / o extensibilidad es el codificador que produjo exactamente la biblioteca que probablemente tenga que arreglar o modificar, exactamente el biblioteca donde la capacidad de anular sería más útil.
La cantidad de veces que tuve que escribir un código de solución feo y desesperado (o abandonar el uso y lanzar mi propia solución alternativa) porque no puedo anular mucho, supera con creces la cantidad de veces que me han mordido ( por ejemplo, en Java) anulando donde el diseñador podría no haber considerado que yo podría.
No virtual por defecto hace mi vida más difícil.
ACTUALIZACIÓN: Se ha señalado [bastante correctamente] que en realidad no respondí la pregunta. Entonces, y con disculpas por llegar bastante tarde ...
Quería un poco poder escribir algo conciso como "C # implementa métodos como no virtuales por defecto porque se tomó una mala decisión que valoraba los programas más que los programadores". (Creo que eso podría estar algo justificado en función de algunas de las otras respuestas a esta pregunta, como el rendimiento (optimización prematura, ¿alguien?), O garantizar el comportamiento de las clases).
Sin embargo, me doy cuenta de que solo estaría expresando mi opinión y no esa respuesta definitiva que desea Stack Overflow. Seguramente, pensé, en el nivel más alto, la respuesta definitiva (pero inútil) es:
No son virtuales de forma predeterminada porque los diseñadores del lenguaje tenían que tomar una decisión y eso es lo que eligieron.
Ahora supongo que la razón exacta por la que tomaron esa decisión nunca la vamos a ... ¡oh, espera! ¡La transcripción de una conversación!
Por lo tanto, parecería que las respuestas y comentarios aquí sobre los peligros de anular las API y la necesidad de diseñar explícitamente para la herencia están en el camino correcto, pero a todas les falta un aspecto temporal importante: la principal preocupación de Anders era mantener una clase o API implícita contrato entre versiones . Y creo que en realidad está más preocupado por permitir que la plataforma .Net / C # cambie bajo el código que por el cambio de código de usuario en la parte superior de la plataforma. (Y su punto de vista "pragmático" es exactamente lo opuesto al mío porque mira desde el otro lado).
(¿Pero no podrían simplemente haber elegido virtual por defecto y luego salpicado de "final" a través de la base de código? Quizás eso no sea lo mismo ... y Anders es claramente más inteligente que yo, así que lo dejaré mentir).
fuente
virtual
... ¿Complemento de Visual Studio, alguien?Porque es demasiado fácil olvidar que un método puede ser anulado y no diseñado para eso. C # te hace pensar antes de hacerlo virtual. Creo que esta es una gran decisión de diseño. Algunas personas (como Jon Skeet) incluso han dicho que las clases deberían estar selladas por defecto.
fuente
Para resumir lo que otros dijeron, hay algunas razones:
1- En C #, hay muchas cosas en sintaxis y semántica que vienen directamente de C ++. El hecho de que los métodos no fueran virtuales de forma predeterminada en C ++ influyó en C #.
2- Tener todos los métodos virtuales por defecto es una preocupación de rendimiento porque cada llamada a un método debe usar la Tabla Virtual del objeto. Además, esto limita en gran medida la capacidad del compilador Just-In-Time para incorporar métodos y realizar otros tipos de optimización.
3- Lo más importante, si los métodos no son virtuales por defecto, puedes garantizar el comportamiento de tus clases. Cuando son virtuales de forma predeterminada, como en Java, ni siquiera puede garantizar que un método getter simple funcione como se espera porque podría anularse para hacer cualquier cosa en una clase derivada (por supuesto, puede, y debe, hacer el método y / o la clase final).
Uno podría preguntarse, como mencionó Zifre, por qué el lenguaje C # no dio un paso más y selló las clases por defecto. Eso es parte de todo el debate sobre los problemas de la herencia de implementación, que es un tema muy interesante.
fuente
C # está influenciado por C ++ (y más). C ++ no habilita el envío dinámico (funciones virtuales) de forma predeterminada. Un argumento (¿bueno?) Para esto es la pregunta: "¿Con qué frecuencia implementa clases que son miembros de una jerarquía de clases?". Otra razón para evitar habilitar el despacho dinámico de forma predeterminada es la huella de memoria. Una clase sin un puntero virtual (vpointer) que apunte a una tabla virtual , es por supuesto más pequeña que la clase correspondiente con enlace tardío habilitado.
No es tan fácil decir "sí" o "no" a la cuestión del rendimiento. La razón de esto es la compilación Just In Time (JIT), que es una optimización del tiempo de ejecución en C #.
Otra pregunta similar sobre la " velocidad de las llamadas virtuales ... "
fuente
La simple razón es el costo de diseño y mantenimiento además de los costos de desempeño. Un método virtual tiene un costo adicional en comparación con un método no virtual porque el diseñador de la clase debe planificar lo que sucede cuando el método es anulado por otra clase. Esto tiene un gran impacto si espera que un método en particular actualice el estado interno o tenga un comportamiento particular. Ahora tiene que planificar lo que sucede cuando una clase derivada cambia ese comportamiento. Es mucho más difícil escribir código confiable en esa situación.
Con un método no virtual tienes el control total. Todo lo que sale mal es culpa del autor original. El código es mucho más fácil de razonar.
fuente
Si todos los métodos de C # fueran virtuales, el vtbl sería mucho mayor.
Los objetos de C # solo tienen métodos virtuales si la clase tiene métodos virtuales definidos. Es cierto que todos los objetos tienen información de tipo que incluye un equivalente de vtbl, pero si no se definen métodos virtuales, solo estarán presentes los métodos de Objeto base.
@Tom Hawtin: Probablemente sea más exacto decir que C ++, C # y Java son todos de la familia de lenguajes C :)
fuente
Viniendo de un fondo de perl, creo que C # selló la perdición de todos los desarrolladores que podrían haber querido extender y modificar el comportamiento de una clase base 'a través de un método no virtual sin forzar a todos los usuarios de la nueva clase a estar al tanto de potencialmente detrás de escena detalles.
Considere el método Add de la clase List. ¿Qué pasa si un desarrollador quisiera actualizar una de varias bases de datos potenciales cada vez que se 'agrega' una lista en particular? Si 'Agregar' hubiera sido virtual por defecto, el desarrollador podría desarrollar una clase 'BackedList' que anulara el método 'Agregar' sin forzar a todo el código del cliente a saber que era una 'BackedList' en lugar de una 'Lista' normal. Para todos los propósitos prácticos, la 'BackedList' se puede ver simplemente como otra 'Lista' del código del cliente.
Esto tiene sentido desde la perspectiva de una clase principal grande que podría proporcionar acceso a uno o más componentes de la lista que están respaldados por uno o más esquemas en una base de datos. Dado que los métodos de C # no son virtuales de forma predeterminada, la lista proporcionada por la clase principal no puede ser una simple IEnumerable o ICollection o incluso una instancia de List, sino que debe anunciarse al cliente como una 'BackedList' para garantizar que la nueva versión de la operación 'Agregar' se llama para actualizar el esquema correcto.
fuente
B
esA
. SiB
requiere algo diferente,A
entonces no lo esA
. Creo que anular como capacidad en el lenguaje en sí es un defecto de diseño. Si necesita unAdd
método diferente , entonces su clase de colección no es unaList
. Tratar de decirlo es fingir. El enfoque correcto aquí es la composición (y no la falsificación). Es cierto que todo el marco se basa en capacidades primordiales, pero simplemente no me gusta.Ciertamente no es un problema de rendimiento. El intérprete de Java de Sun usa el mismo código para enviar (código de
invokevirtual
bytes) y HotSpot genera exactamente el mismo código, yafinal
sea que no. Creo que todos los objetos de C # (pero no las estructuras) tienen métodos virtuales, por lo que siempre necesitará lavtbl
identificación de clase / runtime. C # es un dialecto de "lenguajes similares a Java". Sugerir que proviene de C ++ no es del todo honesto.Existe la idea de que debería "diseñar para la herencia o prohibirla". Lo cual suena como una gran idea hasta el momento en que tiene un caso de negocios serio para solucionarlo rápidamente. Quizás heredando un código que no controlas.
fuente
final
efectivosfinal
es trivial. También vale la pena señalar que puede hacer alineación bimórfica, es decir, métodos en línea con dos implementaciones diferentes.