Pregunta

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 recuerda haber leído un párrafo de Anders Hejlsberg sobre varias ventajas que la arquitectura existente está poniendo de manifiesto. Pero, ¿qué pasa con los efectos secundarios? ¿Es realmente una buena compensación tener métodos no virtuales de forma predeterminada?

¿Fue útil?

Solución

Las clases deben estar diseñadas para que la herencia pueda aprovecharlas. Tener los métodos virtual por defecto significa que cada función de la clase puede ser desconectada y reemplazada por otra, lo que no es realmente una buena cosa. Muchas personas incluso creen que las clases deberían haber sido selladas de forma predeterminada.

Los métodos

virtual también pueden tener una ligera implicación en el rendimiento. Sin embargo, no es probable que esta sea la razón principal.

Otros consejos

Me sorprende que parezca que hay tal consenso aquí de que lo no virtual por defecto es la forma correcta de hacer las cosas. Voy a bajar por el otro lado, creo pragmático, de la cerca.

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

Me parece que el programador que no sabía lo suficiente (o no tenía suficiente tiempo) para diseñar su biblioteca para herencia y / o extensibilidad es el programador que produjo exactamente la biblioteca que probablemente tenga que arreglar o modificar. - exactamente la biblioteca en la que sería más útil la capacidad de anular.

La cantidad de veces que tuve que escribir un código de trabajo feo y desesperado (o abandonar el uso y lanzar mi propia solución alternativa) porque no puedo anular mucho, mucho más que la cantidad de veces que he ha sido mordido (p. ej., en Java) anulando donde el diseñador no haya considerado que podría hacerlo.

Por defecto, lo no virtual hace que mi vida sea más difícil.

ACTUALIZACIÓN: Se ha señalado [bastante bien] que en realidad no respondí la pregunta. Entonces, y con mis 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 más los programas que los programadores " ;. (Creo que podría estar justificado de alguna manera 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 simplemente estaría expresando mi opinión y no esa respuesta definitiva que desea el Desbordamiento de Pila. Seguramente, pensé, en el nivel más alto la respuesta definitiva (pero no útil) es:

De forma predeterminada, no son virtuales porque los diseñadores de idiomas tuvieron 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 lo haremos ... ¡oh, espera! ¡La transcripción de una conversación!

Por lo tanto, parece que las respuestas y los comentarios aquí sobre los peligros de anular las API y la necesidad de diseñar explícitamente para la herencia van por el camino correcto, pero a todos les falta un aspecto temporal importante: la principal preocupación de Anders era mantener el nivel de una clase. o el contrato implícito de API en todas las versiones . Y creo que en realidad está más preocupado por permitir que la plataforma .Net / C # cambie bajo el código en lugar de preocuparse por el cambio del código de usuario en la parte superior de la plataforma. (Y su punto de vista "pragmático" es exactamente lo contrario al mío porque está mirando desde el otro lado).

(¿Pero no podrían simplemente haber elegido virtual por defecto y luego salpicar " final " a través del código base? Tal vez no sea exactamente lo mismo ... y Anders es claramente más inteligente que yo, así que voy a dejar que miente.)

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 de forma predeterminada.

Para resumir lo que otros dijeron, hay algunas razones:

1- En C #, hay muchas cosas en la sintaxis y la semántica que vienen directamente de C ++. El hecho de que los métodos en los que no eran virtuales por defecto en C ++ influyeron en C #.

2- Tener cada método virtual de forma predeterminada es un problema de rendimiento porque cada llamada de método debe usar la Tabla Virtual del objeto. Además, esto limita en gran medida 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 de forma predeterminada, puedes garantizar el comportamiento de tus clases. Cuando son virtuales de forma predeterminada, como en Java, ni siquiera se puede garantizar que un método getter simple funcionará según lo previsto, ya que podría anularse para hacer cualquier cosa en una clase derivada (por supuesto, puede y debe hacerlo). 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 las clases se sellaron 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.

C # está influenciado por C ++ (y más). C ++ no habilita el envío dinámico (funciones virtuales) de forma predeterminada. Un (¿bueno?) Argumento para esto es la pregunta: " ¿Con qué frecuencia implementas clases que son miembros de una investigación de clase? & Quot; Otra razón para evitar habilitar el envío dinámico de forma predeterminada es la huella de memoria. Una clase sin un puntero virtual (vpointer) que apunta a una tabla virtual , es por supuesto más pequeña que la clase correspondiente con vinculación tardía habilitada.

El problema de rendimiento no es tan fácil de decir " sí " o " no " a. El motivo 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 acerca de " velocidad de las llamadas virtuales .. "

El motivo 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 planear qué sucederá cuando otra clase anule el método. Esto tiene un gran impacto si espera que un método particular actualice el estado interno o tenga un comportamiento particular. Ahora tiene que planear 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 control total. Cualquier cosa que salga mal es culpa del autor original. El código es mucho más fácil de razonar.

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 de vtbl, pero si no se definen métodos virtuales, solo estarán presentes los métodos básicos del objeto.

@Tom Hawtin: probablemente sea más exacto decir que C ++, C # y Java son todos de la familia de lenguajes C :)

Partiendo de un fondo de Perl, creo que C # selló el destino 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 obligar a todos los usuarios de la nueva clase a ser conscientes de detras de los detalles de la escena.

Considere la clase de lista 'Agregar método. ¿Qué pasaría si un desarrollador quisiera actualizar una de varias posibles bases de datos 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 anuló el método 'Agregar' sin forzar a todos los códigos del cliente a saber que era una 'Lista Backed' en lugar de una 'Lista' regular. Para todos los propósitos prácticos, la 'Lista respaldada' se puede ver como simplemente otra 'Lista' del código del cliente.

Esto tiene sentido desde la perspectiva de una gran clase principal 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 un simple IEnumerable o ICollection o incluso una instancia de la Lista, sino que debe publicarse al cliente como una "Lista respaldada" para garantizar que la nueva versión de la operación 'Agregar' se llama para actualizar el esquema correcto.

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

Hay una idea que debes "diseñar para la herencia o prohibirla". Lo que suena como una gran idea hasta el momento en que tiene un caso de negocios grave para poner en una solución rápida. Tal vez heredar de un código que no controlas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top