method - C#static member "inheritance": ¿por qué existe esto?
inherited method c# (7)
En C #, los miembros estáticos de una superclase se "heredan" en el alcance de las subclases. Por ejemplo:
class A { public static int M() { return 1; } }
class B : A {}
class C : A { public new static int M() { return 2; } }
[...]
A.M(); //returns 1
B.M(); //returns 1 - this is equivalent to A.M()
C.M(); //returns 2 - this is not equivalent to A.M()
Ahora bien, no puede heredar clases estáticas, y el único lugar que puedo imaginar que la herencia estática podría importar lo ignora por completo: aunque puede hacer una restricción genérica que requiere que un parámetro de tipo T
sea una subclase de A
, aún no puede invocar TM()
(que probablemente simplifica las cosas para la VM), y mucho menos escribir una implementación M
diferente en una subclase y usar eso.
Por lo tanto, la "herencia" de los miembros estáticos simplemente parece contaminación del espacio de nombres; incluso si califica explícitamente el nombre (es decir, BM
), la versión de A todavía se resuelve.
Editar comparar con espacios de nombres:
namespace N1{ class X(); }
namespace N1.N2 { class X(); }
namespace N1.N2.N3 { [...] }
Dentro de N1.N2.N3
Tiene sentido que si uso X
sin calificación se refiere a N1.N2.X
Pero si me refiero explícitamente a N1.N2.N3.X
, y no existe tal clase, no espero que encuentre la versión de N2
; y de hecho al compilador informa un error si intenta esto. Por el contrario, si me refiero explícitamente a BM()
, ¿por qué el compilador no informa un error? Después de todo, no hay un método "M" en "B" ...
¿Qué propósito tiene esta herencia? ¿Puede esta característica ser utilizada constructivamente de alguna manera?
Por lo tanto, la "herencia" de miembros estáticos simplemente se ve como contaminación del espacio de nombres
Así es, excepto que la contaminación de un tipo es el sabor picante adicional de otro tipo.
Creo que Martin Fowler, en su trabajo sobre DSL, ha sugerido usar la herencia de esta manera para permitir el acceso conveniente a métodos estáticos, permitiendo que esos métodos se usen sin calificación de nombre de clase. Por lo tanto, el código de llamada debe estar en una clase que hereda la clase en la que se definen los métodos. (Creo que es una idea podrida)
En mi opinión, los miembros estáticos no deberían mezclarse en una clase con un propósito no estático, y el problema que planteas aquí es parte de la razón por la cual es importante no mezclarlos.
Ocultar datos mutables estáticos privados dentro de la implementación de una clase de "instancia" es particularmente horrible. Pero luego hay métodos estáticos, que son mezcladores aún peores. Aquí hay un uso típico de métodos estáticos mezclados en una clase:
public class Thing
{
// typical per-instance stuff
int _member1;
protected virtual void Foo() { ... }
public void Bar() { ... }
// factory method
public static Thing Make()
{
return new Thing();
}
}
Es el patrón de método de fábrica estático. No tiene sentido la mayor parte del tiempo, pero lo peor es que ahora tenemos esto:
public class AnotherThing : Thing { }
Esto ahora tiene un método de AnotherThing
estático que devuelve una Thing
, no un AnotherThing
.
Este tipo de desajuste implica que cualquier cosa con métodos estáticos debe sellarse. Los miembros estáticos no se integran bien con la herencia. No tiene sentido que sean heredables. Así que guardo las cosas estáticas en clases estáticas separadas, y me quejo de tener que declarar de forma redundante que cada miembro está estático cuando ya he dicho que la clase es estática.
Pero es solo una de esas cosas demasiado tardías. Todos los idiomas de trabajo (y bibliotecas y productos) reales tienen algunos de ellos. C # tiene notablemente pocos.
Creo que es para acceder a miembros estáticos protegidos de la clase base.
class Base
{
protected static void Helper(string s)
{
Console.WriteLine(s);
}
}
class Subclass : Base
{
public void Run()
{
Helper("From the subclass");
}
}
En realidad, tal como lo entiendo, esto es solo un atajo provisto por el compilador. Sintaxis de azúcar. BM()
simplemente compilará a AM()
ya que B no tiene una static M()
. Es para escribir más fácilmente, nada más. No hay "herencia estática".
Agregado: Y el requisito de new
al "redefinir" es solo para que no te dispares accidentalmente en el pie.
Entonces ... ¿Cuál es la alternativa?
La pregunta menciona ...
¿por qué el compilador no informa un error? Después de todo, no hay un método "M" en "B" ...
Pero hay un método derivado "M" en la clase "B" .
Si el compilador no le presentó al programador una tabla virtual unificada para casos base, entonces el programador tendría que ir a buscar tipos de bases para encontrar métodos estáticos. Esto rompería el polymorphism .
Wikipedia ...
El polimorfismo de subtipo, casi universalmente llamado solo polimorfismo en el contexto de la programación orientada a objetos, es la capacidad de un tipo, A, para aparecer como y ser usado como otro tipo, B ...
En los lenguajes fuertemente tipados, el polimorfismo generalmente significa que el tipo A de alguna manera deriva del tipo B, o el tipo C implementa una interfaz que representa el tipo B.
Prefiero tener acceso a todos mis miembros estáticos basados en clases derivadas. De lo contrario, necesitaría saber exactamente dónde se definió el miembro estático y llamarlo explícitamente.
Al usar Intellisense, puede conocer automáticamente cada miembro estático disponible para ese tipo de clase.
Por supuesto, no son heredados, es solo un atajo
Siempre veo que es un medio de prevenir cualquier forma de polimorfismo por parte de la clase heredera en aquellos elementos en los que desea conservar la misma función para todas las clases secundarias.
ignorar lo anterior por alguna razón que estaba pensando en sellado en lugar de estática
Supongo que usaría variables y funciones de miembros estáticos para garantizar que cualquier dato o funcionalidad no dependa de la instancia de una clase, ya que solo se instanciaría una vez.
Un ejemplo de uso sería decir un valor de contador que mantendría un conteo real de todas las instancias de las subclases de una superclase (cada subclase incrementa el valor de conteo estático en la construcción). Este valor de recuento estaría disponible e igual para todas las instancias de la subclase.
Así es como funciona , probablemente sería una respuesta estúpida en la mayoría de los casos. Pero en este caso, es cómo funciona; dado que deriva de A, dice que es A + las características adicionales que agrega.
Por lo tanto, debe poder acceder a las mismas variables que a través de una instancia de A.
Sin embargo, heredar una clase estática no tiene sentido mientras que el acceso a los miembros / campos / métodos estáticos lo hace.
Un ejemplo de esto es lo siguiente:
internal class BaseUser
{
public static string DefaultUserPool { get; set; }
}
internal class User : BaseUser
{
public int Id { get; set; }
public string Name { get; set; }
public User Parent { get; set; }
}
Donde la prueba se ve así:
User.DefaultUserPool = "Test";
BaseUser.DefaultUserPool = "Second Test";
Console.WriteLine(User.DefaultUserPool);
Console.WriteLine(BaseUser.DefaultUserPool);
Ambas líneas WriteLines emiten "Segunda prueba", esto se debe a que tanto BaseUser como User deben usar DefaultUserPool, por diseño . Y reemplazar los métodos estáticos implementados no tendría sentido, ya que es solo un accesorio en la clase infantil.
Sólo puede haber uno. Anularlo significaría que hay una nueva implementación para esa subclase, lo que mataría el término "estático".