¿Por qué C# implementa métodos como no virtuales de forma predeterminada?


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 rendimiento en lugar de otros resultados posibles?

Me acuerdo de leer un párrafo de Anders Hejlsberg sobre varias ventajas que la arquitectura existente está sacando a la luz. Pero, ¿qué pasa con los efectos secundarios? ¿Es realmente una buena compensación tener métodos no virtuales por defecto?

Author: kevinarpe, 2009-05-02

9 answers

Las clases deben ser diseñadas para la herencia para poder aprovecharla. Tener métodos virtual por defecto significa que cada función de la clase puede ser enchufada y reemplazada por otra, lo cual no es realmente bueno. Muchas personas incluso creen que las clases deberían haber sido sealed 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.

 96
Author: Mehrdad Afshari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-04 22:03:23

Me sorprende que parece haber un consenso tal aquí que no-virtual-por-defecto es la manera correcta de hacer las cosas. Voy a bajar al otro lado-creo que pragmático-de la cerca.

La mayoría de las justificaciones me leen como el viejo argumento "Si te damos el poder, podrías lastimarte a ti mismo". ¿De programadores?!

Me parece que el codificador que no sabía lo suficiente (o no tenía tiempo suficiente) para diseñar su biblioteca para la herencia y / o extensibilidad es el codificador que ha producido exactamente la biblioteca que es probable que tenga que arreglar o ajustar - exactamente la biblioteca donde la capacidad de anular sería más útil.

El número de veces que he tenido que escribir código de trabajo feo y desesperado (o abandonar el uso y rodar mi propia solución alternativa) porque no puedo anular mucho, mucho supera el número de veces que he sido mordido (por ejemplo, en Java) al anular donde el diseñador podría no haber considerado que podría.

No-virtual-por-defecto me hace la vida más difícil.

ACTUALIZACIÓN: Se ha señalado [muy correctamente] que en realidad no respondí a la pregunta. Así que apologies y con disculpas por llegar tarde....

Quería ser capaz de 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 base a algunas de las otras respuestas a esta pregunta-como rendimiento (optimización prematura, ¿alguien?), o garantizar el comportamiento de las clases.)

Sin embargo, me doy cuenta de que solo estaría declarando mi opinión y no esa respuesta definitiva que desea Desbordar la Pila. Seguramente, pensé, en el nivel más alto la respuesta definitiva (pero inútil) es:

No son virtuales por defecto 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.... oh, espera! La transcripción de una conversación!

Así que parece que las respuestas y comentarios aquí sobre los peligros de sobreescribir las API y la necesidad de diseñar explícitamente para la herencia están en el camino correcto, pero a todos les falta un aspecto temporal importante: la principal preocupación de Anders era mantener el contrato implícito de una clase o API a través de las versiones. Y creo que en realidad está más preocupado por permitir que la plataforma. net / C# cambie bajo código en lugar de preocuparse 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 está mirando desde el otro lado.)

(¿Pero no podrían haber elegido virtual por defecto y luego salpicado "final" a través de la base de código? Quizá no sea lo mismo.. y Anders es claramente más inteligente que yo así que voy a dejar que mienta.)

 82
Author: mwardm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-10 11:59:03

Porque es demasiado fácil olvidar que un método puede ser anulado y no diseñarse 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 deben ser selladas por defecto.

 16
Author: Zifre,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-02 14:35:35

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 sean virtuales por defecto en C++ influyó en C#.

2- Tener cada método virtual de forma predeterminada es un problema de rendimiento porque cada llamada a método debe usar la Tabla virtual del objeto. Además, esto limita fuertemente la capacidad del compilador Just-In-Time para integrar métodos y realizar otros tipos de optimización.

3- Lo más importante es que si los métodos no son virtuales por defecto, puede garantizar el comportamiento de sus clases. Cuando son virtuales por defecto, como en Java, ni siquiera se puede garantizar que un método getter simple hará lo previsto porque podría ser anulado para hacer cualquier cosa en una clase derivada (por supuesto que puede, y debe, hacer que el método y/o la clase final).

Uno podría preguntarse, como mencionó Zifre, por qué el lenguaje C# no da un paso más y haz que las clases estén selladas de forma predeterminada. Eso es parte de todo el debate sobre los problemas de la herencia de implementación, que es un tema muy interesante.

 10
Author: Trillian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-02 14:41:39

C# está influenciado por C++ (y más). C++ no habilita el despacho dinámico (funciones virtuales) de forma predeterminada. Uno (bueno? el argumento para esto es la pregunta: "¿Con qué frecuencia implementas clases que son miembros de una clase hiearchy?". 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) apuntando a una tabla virtual , es por supuesto más pequeña que la clase correspondiente con el enlace tardío habilitado.

El problema de rendimiento no es tan fácil decir "sí" o "no". La razón de esto es la compilación Just In Time (JIT) que es una optimización en tiempo de ejecución en C#.

Otra pregunta similar sobre " velocidad de las llamadas virtuales.."

 9
Author: Schildmeijer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:56

La razón simple es el costo de diseño y mantenimiento, además de los costos de rendimiento. 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 reemplazado 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 tienes 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. Cualquier cosa que salga mal es culpa del autor original. El código es mucho más fácil de razonar.

 5
Author: JaredPar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-02 14:46:27

Si todos los métodos de C# fueran virtuales, entonces el vtbl sería mucho más grande.

Los objetos 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 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 preciso decir que C++, C# y Java son todos de la familia de lenguajes C:)

 2
Author: Thomas Bratt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-02 20:10:47

Ciertamente no es un problema de rendimiento. El intérprete Java de Sun utiliza el mismo código para enviar (invokevirtual bytecode) y HotSpot genera exactamente el mismo código, ya sea final o no. Creo que todos los objetos de C# (pero no las estructuras) tienen métodos virtuales, por lo que siempre necesitará la identificación de la clase vtbl/runtime. C# es un dialecto de "lenguajes similares a Java". Sugerir que viene de C++ no es del todo honesto.

Hay una idea que usted debe " diseñar para la herencia o de lo contrario prohibirlo". Lo que suena como una gran idea hasta el momento en que tiene un caso de negocio grave para poner en una solución rápida. Quizás heredando de código que no controlas.

 1
Author: Tom Hawtin - tackline,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-02 14:47:46

Viniendo de un fondo perl creo que C# selló el destino de cada desarrollador que podría 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 ser conscientes de los detalles potencialmente detrás de la escena.

Considere la clase List' Add method. ¿Qué pasaría si un desarrollador quisiera actualizar una de varias bases de datos potenciales cada vez que se "Agrega" una Lista en particular? Si ' Add ' había sido virtual por defecto el desarrollador podría desarrolle una clase' BackedList 'que anule el método' Add '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 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 lista que a su vez están respaldados por uno o más esquemas en una base de datos. Dado que los métodos de C# no son virtuales por defecto, la lista proporcionada por la clase main no puede ser una simple ICumerable o ICollection o incluso una instancia de Lista, sino que debe anunciarse al cliente como una 'BackedList' para garantizar que la nueva versión de la operación 'Add' se llame para actualizar el esquema correcto.

 1
Author: Travis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-03 15:17:04