¿Por qué no puedo tener métodos estáticos abstractos en C #?

Últimamente he trabajado un poco con proveedores , y encontré una situación interesante en la que quería tener una clase abstracta que tuviera un método estático abstracto. Leí algunas publicaciones sobre el tema, y ​​tuvo sentido, pero ¿hay una buena explicación clara?

Los métodos estáticos no se instancian como tales, solo están disponibles sin una referencia de objeto.

Una llamada a un método estático se realiza a través del nombre de clase, no a través de una referencia de objeto, y el código IL para llamarlo llamará al método abstracto por el nombre de la clase que lo definió, no necesariamente el nombre de la clase que utilizó .

Déjame mostrar un ejemplo.

Con el siguiente código:

public class A { public static void Test() { } } public class B : A { } 

Si llama a B.Test, haga lo siguiente:

 class Program { static void Main(string[] args) { B.Test(); } } 

Entonces, el código real dentro del método Main es el siguiente:

 .entrypoint .maxstack 8 L0000: nop L0001: call void ConsoleApplication1.A::Test() L0006: nop L0007: ret 

Como puede ver, la llamada se realiza en A.Test, porque fue la clase A que lo definió, y no B.Test, aunque puede escribir el código de esa manera.

Si tuviera tipos de clases , como en Delphi, donde puede hacer una variable que se refiera a un tipo y no a un objeto, tendría más uso para métodos estáticos virtuales y, por lo tanto, abstractos (y también constructores), pero no están disponibles y por lo tanto, las llamadas estáticas no son virtuales en .NET.

Me doy cuenta de que los diseñadores de IL podrían permitir que el código se comstackra para llamar a B.Test y resolver la llamada en tiempo de ejecución, pero aún así no sería virtual, ya que todavía tendría que escribir algún tipo de nombre de clase allí.

Los métodos virtuales, y por lo tanto los abstractos, solo son útiles cuando usa una variable que, en tiempo de ejecución, puede contener muchos tipos diferentes de objetos, y por lo tanto desea llamar al método correcto para el objeto actual que tiene en la variable. Con los métodos estáticos, es necesario pasar por un nombre de clase de todos modos, por lo que el método exacto para llamar se conoce en tiempo de comstackción porque no puede y no cambiará.

Por lo tanto, los métodos estáticos virtuales / abstractos no están disponibles en .NET.

Los métodos estáticos no se pueden heredar ni anular, y es por eso que no pueden ser abstractos. Como los métodos estáticos están definidos en el tipo, no en la instancia, de una clase, deben llamarse explícitamente en ese tipo. Entonces, cuando desee llamar a un método en una clase secundaria, debe usar su nombre para llamarlo. Esto hace que la herencia sea irrelevante.

Supongamos que puede, por un momento, heredar métodos estáticos. Imagina este escenario:

 public static class Base { public static virtual int GetNumber() { return 5; } } public static class Child1 : Base { public static override int GetNumber() { return 1; } } public static class Child2 : Base { public static override int GetNumber() { return 2; } } 

Si llama a Base.GetNumber (), ¿qué método se llamaría? ¿Qué valor devolvió? Es bastante fácil ver que sin crear instancias de objetos, la herencia es bastante difícil. Los métodos abstractos sin herencia son solo métodos que no tienen un cuerpo, por lo que no se pueden llamar.

Otro encuestado (McDowell) dijo que el polymorphism solo funciona para instancias de objetos. Eso debería ser calificado; hay idiomas que tratan las clases como instancias de tipo “Clase” o “Metaclases”. Estos lenguajes admiten polymorphism para métodos de instancia y clase (estáticos).

C #, como Java y C ++ antes que él, no es ese lenguaje; la palabra clave static se usa explícitamente para indicar que el método está vinculado estáticamente en lugar de dynamic / virtual.

Para agregar a las explicaciones anteriores, las llamadas a métodos estáticos están vinculadas a un método específico en tiempo de comstackción , que descarta bastante el comportamiento polimórfico.

Aquí hay una situación en la que definitivamente hay una necesidad de herencia para los campos y métodos estáticos:

 abstract class Animal { protected static string[] legs; static Animal() { legs=new string[0]; } public static void printLegs() { foreach (string leg in legs) { print(leg); } } } class Human: Animal { static Human() { legs=new string[] {"left leg", "right leg"}; } } class Dog: Animal { static Dog() { legs=new string[] {"left foreleg", "right foreleg", "left hindleg", "right hindleg"}; } } public static void main() { Dog.printLegs(); Human.printLegs(); } //what is the output? //does each subclass get its own copy of the array "legs"? 

En realidad, reemplazamos los métodos estáticos (en Delphi), es un poco feo, pero funciona perfectamente para nuestras necesidades.

Lo usamos para que las clases puedan tener una lista de sus objetos disponibles sin la instancia de la clase, por ejemplo, tenemos un método que se ve así:

 class function AvailableObjects: string; override; begin Result := 'Object1, Object2'; end; 

Es feo pero necesario, de esta manera podemos instanciar lo que se necesita, en lugar de tener todas las clases instantáneas solo para buscar los objetos disponibles.

Este fue un ejemplo simple, pero la aplicación en sí es una aplicación cliente-servidor que tiene todas las clases disponibles en un solo servidor y múltiples clientes diferentes que pueden no necesitar todo lo que tiene el servidor y nunca necesitará una instancia de objeto.

Por lo tanto, es mucho más fácil de mantener que tener una aplicación de servidor diferente para cada cliente.

Espero que el ejemplo sea claro.

Los métodos abstractos son implícitamente virtuales. Los métodos abstractos requieren una instancia, pero los métodos estáticos no tienen una instancia. Entonces, puede tener un método estático en una clase abstracta, simplemente no puede ser un resumen estático (o estático abstracto).