¿Por qué los métodos estáticos no deben poder sobrescribirse?

13

En las respuestas a this pregunta, el consenso general fue que los métodos estáticos no deben ser anulados (y, por lo tanto, las funciones estáticas en C # no pueden ser virtuales o abstractas). Sin embargo, este no es solo el caso en C #; Java también lo prohíbe y a C ++ tampoco parece gustarle. Sin embargo, puedo pensar en muchos ejemplos de funciones estáticas que me gustaría anular en una clase secundaria (por ejemplo, métodos de fábrica). Si bien en teoría, hay formas de evitarlos, ninguno de ellos es limpio o simple.

¿Por qué las funciones estáticas no deben ser anulables?

    
pregunta Garan 06.09.2016 - 19:01

5 respuestas

14

Con los métodos estáticos, no hay ningún objeto que proporcione un control adecuado del mecanismo de anulación.

El mecanismo de método virtual de clase / instancia normal permite un control preciso de las anulaciones de la siguiente manera: cada objeto real es una instancia de exactamente una clase. Esa clase determina el comportamiento de las anulaciones; siempre obtiene la primera grieta en los métodos virtuales. Entonces puede elegir llamar al método principal en el momento adecuado para su implementación. Cada método padre también recibe su turno para invocar su método padre. Esto da como resultado una bonita cascada de invocaciones principales, que logra una de las nociones de reutilización del código por la que se conoce la orientación a objetos. (En este caso, el código de las clases base / súper se está reutilizando de una manera relativamente compleja; otra noción ortogonal de reutilización de código en OOP es simplemente tener varios objetos de la misma clase).

Las clases base pueden ser reutilizadas por varias subclases, y cada una puede coexistir cómodamente. Cada clase que se utiliza para instanciar objetos dictando su propio comportamiento, coexistiendo de manera pacífica y simultánea con los demás. El cliente tiene control sobre qué comportamientos desea y cuándo elige qué clase usar para crear una instancia de un objeto y pasarlo a otros según lo desee.

(Este no es un mecanismo perfecto, ya que siempre se pueden identificar capacidades que no son compatibles, por lo que es por eso que los patrones como el método de fábrica y la inyección de dependencia están superpuestos).

Por lo tanto, si tuviéramos que crear una capacidad de anulación de estadísticas sin cambiar nada más, tendríamos dificultades para ordenar las anulaciones. Sería difícil definir un contexto limitado para la aplicabilidad de la anulación, por lo que obtendría la anulación global en lugar de hacerlo de forma más local como con los objetos. No hay objeto de instancia para cambiar el comportamiento. Entonces, si alguien invocó el método estático que pasó a ser anulado por otra clase, ¿debería la anulación obtener el control o no? Si hay múltiples anulaciones de este tipo, ¿quién obtiene el control primero? ¿segundo? Con los reemplazos de objetos de instancia, todas estas preguntas tienen respuestas significativas y bien razonadas, pero con las estadísticas no.

Las anulaciones de lo estático serían sustancialmente caóticas, y esto se ha hecho antes.

Por ejemplo, Mac OS System 7 y anteriores utilizaron un mecanismo de parcheo de trampas para extender el sistema al obtener el control de las llamadas al sistema hechas por la aplicación antes del sistema operativo. Puede pensar en la tabla de parches de llamadas del sistema como una matriz de punteros de función, como un vtable para objetos de ejemplo, excepto que era una tabla global única.

Esto causó un dolor incalculable a los programadores debido a la naturaleza desordenada de la trampa de parches. Quien consiguió remendar la última trampa, básicamente ganó, incluso si no quería. Cada parche de la captura capturaría el valor de captura anterior para una capacidad de llamada principal, que era extremadamente frágil. La eliminación de un parche de captura, por ejemplo, cuando ya no necesitaba saber acerca de una llamada del sistema, se consideraba una forma incorrecta, ya que realmente no tenía la información necesaria para eliminar su parche (si lo hiciera, también desempacaría cualquier otro parche que hubiera seguido). usted).

Esto no quiere decir que sería imposible crear un mecanismo para anular las estáticas, pero lo que probablemente preferiría hacer en su lugar es convertir los campos estáticos y los métodos estáticos en campos de instancias y métodos de instancias de metaclases, de modo que Entonces se aplicarían las técnicas normales de orientación a objetos. Tenga en cuenta que hay sistemas que también lo hacen: CSE 341: Clases de conversación pequeña y metaclases ; Vea también: ¿Cuál es el equivalente de Smalltalk de la estática de Java?

Estoy tratando de decir que tendrías que hacer un diseño serio de las características del lenguaje para que funcione incluso razonablemente bien. Por ejemplo, se hizo un enfoque ingenuo, se aflojó, pero fue muy problemático, y posiblemente (es decir, argumentaría) defectuoso arquitectónicamente al proporcionar una abstracción incompleta y difícil de usar.

Cuando termine de diseñar la función de anulaciones estáticas para que funcione bien, es posible que haya inventado alguna forma de metaclases, que es una extensión natural de la POO a / para métodos basados en clases. Por lo tanto, no hay razón para no hacer esto, y algunos idiomas realmente lo hacen. Quizás es solo un poco más de un requisito adicional que varios idiomas eligen no hacer.

    
respondido por el Erik Eidt 06.09.2016 - 19:26
9

La anulación depende del envío virtual: utiliza el tipo de tiempo de ejecución del parámetro this para decidir a qué método llamar. Un método estático no tiene el parámetro this , por lo que no hay nada que enviar.

Algunos idiomas, especialmente Delphi y Python, tienen un alcance "intermedio" que permite esto: métodos de clase. Un método de clase no es un método de instancia común, pero tampoco es estático; recibe un parámetro self (además, ambos idiomas llaman al parámetro this self ) que es una referencia al tipo de objeto en sí, en lugar de a una instancia de ese tipo. Con ese valor, ahora tiene un tipo disponible para realizar un envío virtual.

Lamentablemente, ni la JVM ni la CLR tienen nada comparable.

    
respondido por el Mason Wheeler 06.09.2016 - 19:55
3

Usted pregunta

  

¿Por qué las funciones estáticas no deben ser anulables?

pregunto

  

¿Por qué las funciones que desea reemplazar deben ser estáticas?

Ciertos idiomas te obligan a iniciar el programa en un método estático. Pero después de eso, realmente puedes resolver muchos problemas sin ningún otro método estático.

A algunas personas les gusta usar métodos estáticos en cualquier momento en que no haya dependencia del estado en un objeto. A algunas personas les gusta usar métodos estáticos para la construcción de otros objetos. A algunas personas les gusta evitar los métodos estáticos tanto como sea posible.

Ninguna de estas personas está equivocada.

Si necesita que se pueda anular, simplemente deje de etiquetarlo como estático. Nada va a romperse porque tienes un objeto sin estado volando alrededor.

    
respondido por el candied_orange 06.09.2016 - 20:46
2
  

¿Por qué los métodos estáticos no deberían poder sobrescribirse?

No es una cuestión de "debería".

"Anular" significa "enviar dinámicamente ". "Método estático" significa "envío estáticamente". Si algo es estático, no puede ser anulado. Si algo puede ser anulado, no es estático.

Su pregunta es bastante parecida a la pregunta: "¿Por qué los triciclos no deberían tener cuatro ruedas?" La definición de "triciclo" es que tiene tres ruedas. Si es un triciclo, no puede tener cuatro ruedas, si tiene cuatro ruedas, no puede ser un triciclo. Del mismo modo, la definición de "método estático" es que se envía de forma estática. Si es un método estático, no se puede enviar dinámicamente, si se puede enviar dinámicamente, no puede ser un método estático.

Por supuesto, es perfectamente posible tener métodos de clase que se pueden anular. O bien, podría tener un lenguaje como Ruby, donde las clases son objetos como cualquier otro objeto y, por lo tanto, pueden tener métodos de instancia, lo que elimina completamente la necesidad de los métodos de clase por completo. (Ruby solo tiene un tipo de métodos: métodos de instancia. No tiene métodos de clase, métodos estáticos, constructores, funciones o procedimientos).

    
respondido por el Jörg W Mittag 07.09.2016 - 16:59
1
  

por lo tanto, las funciones estáticas en C # no pueden ser virtuales o abstractas

En C #, siempre llama a miembros estáticos usando la clase, por ejemplo, BaseClass.StaticMethod() , no baseObject.StaticMethod() . Entonces, si tiene ChildClass heredando de BaseClass y childObject una instancia de ChildClass , no podrá llamar a su método estático desde childObject . Siempre necesitarás usar explícitamente la clase real, por lo que un static virtual simplemente no tiene sentido.

Lo que puedes hacer es redefinir el mismo método static en tu clase secundaria y usar la palabra clave new .

class BaseClass {
    public static int StaticMethod() { return 1; }
}

class ChildClass {
    public static new int StaticMethod() { return BaseClass.StaticMethod() + 2; }
}

int result;    
var baseObj = new BaseClass();
var childObj = new ChildClass();

result = BaseClass.StaticMethod();
result = baseObj.StaticMethod(); // DOES NOT COMPILE

result = ChildClass.StaticMethod();
result = childObj.StaticMethod(); // DOES NOT COMPILE

Si fuera posible llamar a baseObject.StaticMethod() , entonces su pregunta tendría sentido.

    
respondido por el user276648 07.09.2016 - 07:49

Lea otras preguntas en las etiquetas