¿Por qué no puedo heredar clases estáticas?

224

Tengo varias clases que realmente no necesitan ningún estado. Desde el punto de vista organizativo, me gustaría ponerlos en jerarquía.

Pero parece que no puedo declarar herencia para clases estáticas.

Algo como eso:

public static class Base
{
}

public static class Inherited : Base
{
}

no trabajará.

¿Por qué los diseñadores del lenguaje han cerrado esa posibilidad?

Usuario
fuente
Posible duplicado de ¿Cómo puede derivarse una clase estática de un objeto?
Musulmán Ben Dhaou

Respuestas:

174

Cita desde aquí :

Esto es en realidad por diseño. Parece que no hay una buena razón para heredar una clase estática. Tiene miembros públicos estáticos a los que siempre puede acceder a través del nombre de la clase. Las únicas razones que he visto para heredar cosas estáticas han sido malas, como guardar un par de caracteres de escritura.

Puede haber razones para considerar mecanismos para llevar miembros estáticos directamente al alcance (y de hecho consideraremos esto después del ciclo del producto Orcas), pero la herencia de clase estática no es el camino a seguir: es el mecanismo incorrecto para usar, y funciona solo para miembros estáticos que residen en una clase estática.

(Mads Torgersen, Lenguaje C # PM)

Otras opiniones del canal 9

La herencia en .NET funciona solo en la base de instancias. Los métodos estáticos se definen en el nivel de tipo, no en el nivel de instancia. Es por eso que la anulación no funciona con métodos / propiedades / eventos estáticos ...

Los métodos estáticos solo se guardan una vez en la memoria. No hay una tabla virtual, etc. creada para ellos.

Si invoca un método de instancia en .NET, siempre le da la instancia actual. Esto está oculto por el tiempo de ejecución de .NET, pero sucede. Cada método de instancia tiene como primer argumento un puntero (referencia) al objeto en el que se ejecuta el método. Esto no sucede con los métodos estáticos (ya que se definen a nivel de tipo). ¿Cómo debe decidir el compilador seleccionar el método para invocar?

(pequeño gurú)

Y como una idea valiosa, littleguru tiene una "solución" parcial para este problema: el patrón Singleton .

boj
fuente
92
Falta de imaginación por parte de Torgersen, de verdad. Tenía una muy buena razón para querer hacer esto :)
Thorarin
10
¿Qué tal esto? Tiene un dll que no es de código abierto / no tiene acceso a su fuente, digamos NUnit. Desea extender su clase Assert para tener un método como Throws <> (Acción a) (sí, eso también está allí, pero en un momento no lo fue). Puede agregar a su proyecto un Assert: NUnit.Framework.Assert clase, y luego agregue un método Throws. Problema resuelto. También es más limpio usarlo en el código ... en lugar de tener que crear otro nombre de clase y recordar ese nombre de clase, simplemente escriba Assert. y ver los métodos disponibles.
user420667
44
@ KonradMorawski Es imposible escribir métodos de extensión para clases estáticas. stackoverflow.com/questions/249222/…
Martin Braun
2
@modiX Por supuesto. Me entendiste mal aquí. Lo que quise decir es que la funcionalidad requerida en el escenario descrito por user420667 podría proporcionarse ( en el futuro ) tan poco como permitir métodos de extensión para clases estáticas. No se necesita herencia estática. Es por eso que su escenario no me pareció una muy buena razón para introducir la herencia estática. Si C # se modificara en este aspecto, ¿por qué optar por un rediseño importante cuando uno menor sería tan bueno en la práctica?
Konrad Morawski
55
@Learner Eso no es realmente justo. En .Net todo se hereda de object, y se espera que todos lo sepan. Entonces, las clases estáticas siempre heredan object, ya sea que lo especifiques explícitamente o no.
RenniePet
71

La razón principal por la que no puede heredar una clase estática es que son abstractos y sellados (esto también evita que se cree cualquier instancia de ellos).

Así que esto:

static class Foo { }

compila a esta IL:

.class private abstract auto ansi sealed beforefieldinit Foo
  extends [mscorlib]System.Object
 {
 }
Andrew Hare
fuente
17
Está diciendo que la estática se implementó como abstracta + sellada. Quiere saber por qué se hizo esto. ¿Por qué está sellado?
Lucas
22
Esto no responde a la pregunta; simplemente reafirma el problema sobre el que estaba preguntando.
Igby Largeman
28
Bueno, el OP debería haber preguntado "¿Por qué las clases estáticas están selladas", no "¿Por qué no puedo heredar de las clases estáticas?", La respuesta a la cual es, por supuesto, "porque están selladas" . Esta respuesta recibe mi voto.
Alex Budovski
66
gracias por compartir que las clases estáticas se compilan automáticamente como clases selladas. ¡Me preguntaba cómo / cuándo sucedió eso!
Mark
23
@AlexBudovski: Esta respuesta es correcta pero tan inútil como decirle a una persona perdida "estás frente a mí" cuando te preguntan "dónde estoy".
DeepSpace101
24

Piénselo de esta manera: accede a miembros estáticos a través del nombre del tipo, así:

MyStaticType.MyStaticMember();

Si heredaras de esa clase, tendrías que acceder a ella a través del nuevo nombre de tipo:

MyNewType.MyStaticMember();

Por lo tanto, el nuevo elemento no tiene relación con el original cuando se usa en el código. No habría forma de aprovechar una relación de herencia para cosas como el polimorfismo.

Quizás esté pensando que solo quiere extender algunos de los elementos de la clase original. En ese caso, no hay nada que le impida simplemente usar un miembro del original en un tipo completamente nuevo.

Quizás desee agregar métodos a un tipo estático existente. Puede hacerlo ya a través de métodos de extensión.

Quizás desee poder pasar una estática Typea una función en tiempo de ejecución y llamar a un método de ese tipo, sin saber exactamente qué hace el método. En ese caso, puede usar una interfaz.

Entonces, al final, realmente no obtienes nada heredando clases estáticas.

Joel Coehoorn
fuente
55
No puede agregar métodos a un tipo estático existente mediante un método de extensión. Consulte mi comentario sobre la respuesta aceptada para ver un ejemplo de uso deseado. (Básicamente, debería cambiar el nombre de lo que ha llamado MyNewType como MyStaticType, por lo que MyStaticType: OldProject.MyStaticType)
user420667
2
Se podrían definir relaciones de herencia con tipos estáticos y miembros estáticos virtuales de tal manera que se SomeClass<T> where T:Foopueda acceder a miembros de Foo, y si Tfuera una clase que anulara algunos miembros estáticos virtuales Foo, la clase genérica usaría esas anulaciones. Probablemente incluso sería posible definir una convención a través de la cual los lenguajes pudieran hacerlo de una manera compatible con el CLR actual (por ejemplo, una clase con tales miembros debería definir una clase no estática protegida que contenga tales miembros de instancia, junto con un campo estático sosteniendo una instancia de ese tipo).
supercat
Me encontré con un caso en el que creo que heredar una clase estática es lo correcto, aunque obviamente puedes acceder a ellos de todos modos. Tengo una clase estática para enviar flujos de datos sin procesar a la impresora (para hablar con impresoras de etiquetas industriales). Tiene un montón de declaraciones de API de Windows. Ahora me encuentro escribiendo otra clase para jugar con impresoras que necesita las mismas llamadas API. ¿Combinar? No está bien. ¿Declararlos dos veces? Feo. ¿Heredar? Bien, pero no permitido.
Loren Pechtel
3

Lo que desea lograr mediante el uso de la jerarquía de clases se puede lograr simplemente a través del espacio de nombres. Por lo tanto, los lenguajes que admiten espacios de nombres (como C #) no tendrán uso de implementar la jerarquía de clases de clases estáticas. Como no puede crear una instancia de ninguna de las clases, todo lo que necesita es una organización jerárquica de las definiciones de clase que puede obtener mediante el uso de espacios de nombres

Amit
fuente
Tengo un cargador genérico que recibe una clase como tipo. Esa clase tiene 5 métodos auxiliares y 2 métodos que devuelven una cadena (nombre y descripción). Podría haberlo escogido mal (creo que sí), pero la única solución que encontré fue crear una instancia de la clase en el cargador genérico, para acceder a los métodos ... meh.
ANeves el
La alternativa sería un diccionario gigante, supongo. Además, cuando diga "lo que quiere lograr", debería decir "a lo que parece estar apuntando" o algo similar.
ANeves
3

Puede usar composición en su lugar ... esto le permitirá acceder a objetos de clase desde el tipo estático. Pero aún no puede implementar interfaces o clases abstractas

Yogesh Karekar
fuente
2

Aunque puede acceder a miembros estáticos "heredados" a través del nombre de las clases heredadas, los miembros estáticos no se heredan realmente. Esto es en parte por qué no pueden ser virtuales o abstractos y no pueden ser anulados. En su ejemplo, si declaró un Base.Method (), el compilador asignará una llamada a Inherited.Method () de nuevo a Base.Method () de todos modos. También podría llamar a Base.Method () explícitamente. Puede escribir una pequeña prueba y ver el resultado con Reflector.

Entonces ... si no puede heredar miembros estáticos, y si las clases estáticas solo pueden contener miembros estáticos, ¿de qué serviría heredar una clase estática?

Lucas
fuente
1

Hmmm ... ¿sería muy diferente si solo tuvieras clases no estáticas llenas de métodos estáticos ...?

CookieOfFortune
fuente
2
Eso es lo que me queda. Lo hago de esta manera. Y no me gusta.
Usuario
También lo he hecho de esta manera, pero por alguna razón no parece estético cuando sabes que nunca crearás una instancia del objeto. Siempre me preocupan detalles como este, a pesar del hecho de que no tiene un costo material.
gdbj
En una clase no estática llena de métodos estáticos, no puede poner métodos de extensión :)
Jakub Szułakiewicz
1

Una solución alternativa que puede hacer es no usar clases estáticas, sino ocultar el constructor para que los miembros estáticos de las clases sean lo único accesible fuera de la clase. El resultado es una clase "estática" heredable esencialmente:

public class TestClass<T>
{
    protected TestClass()
    { }

    public static T Add(T x, T y)
    {
        return (dynamic)x + (dynamic)y;
    }
}

public class TestClass : TestClass<double>
{
    // Inherited classes will also need to have protected constructors to prevent people from creating instances of them.
    protected TestClass()
    { }
}

TestClass.Add(3.0, 4.0)
TestClass<int>.Add(3, 4)

// Creating a class instance is not allowed because the constructors are inaccessible.
// new TestClass();
// new TestClass<int>();

Desafortunadamente debido a la limitación del lenguaje "por diseño" no podemos hacer:

public static class TestClass<T>
{
    public static T Add(T x, T y)
    {
        return (dynamic)x + (dynamic)y;
    }
}

public static class TestClass : TestClass<double>
{
}
EnfocadoLobo
fuente
1

Puede hacer algo que se verá como herencia estática.

Aquí está el truco:

public abstract class StaticBase<TSuccessor>
    where TSuccessor : StaticBase<TSuccessor>, new()
{
    protected static readonly TSuccessor Instance = new TSuccessor();
}

Entonces puedes hacer esto:

public class Base : StaticBase<Base>
{
    public Base()
    {
    }

    public void MethodA()
    {
    }
}

public class Inherited : Base
{
    private Inherited()
    {
    }

    public new static void MethodA()
    {
        Instance.MethodA();
    }
}

La Inheritedclase no es estática en sí misma, pero no permitimos crearla. En realidad, ha heredado el constructor estático que construye Base, y todas las propiedades y métodos deBase disponibles como estáticos. Ahora, lo único que queda por hacer son envoltorios estáticos para cada método y propiedad que necesita exponer a su contexto estático.

Hay inconvenientes como la necesidad de crear manualmente los métodos de envoltura estática y las newpalabras clave. Pero este enfoque ayuda a soportar algo que es realmente similar a la herencia estática.

PD: Usamos esto para crear consultas compiladas, y esto en realidad se puede reemplazar con ConcurrentDictionary, pero un campo de solo lectura estático con su seguridad de subprocesos fue lo suficientemente bueno.

Konard
fuente
1

Mi respuesta: mala elección de diseño. ;-)

Este es un debate interesante centrado en el impacto de la sintaxis. El núcleo del argumento, en mi opinión, es que una decisión de diseño condujo a clases estáticas selladas. ¿Un enfoque en la transparencia de los nombres de la clase estática que aparecen en el nivel superior en lugar de esconderse ('confundir') detrás de los nombres secundarios? Uno puede imaginar una implementación de lenguaje que podría acceder a la base o al niño directamente, confuso.

Un pseudo ejemplo, suponiendo que la herencia estática se definió de alguna manera.

public static class MyStaticBase
{
    SomeType AttributeBase;
}

public static class MyStaticChild : MyStaticBase
{
    SomeType AttributeChild;
}

daría lugar a:

 // ...
 DoSomethingTo(MyStaticBase.AttributeBase);
// ...

que podría (afectaría) el mismo almacenamiento que

// ...
DoSomethingTo(MyStaticChild.AttributeBase);
// ...

¡Muy confuso!

¡Pero espera! ¿Cómo trataría el compilador con MyStaticBase y MyStaticChild con la misma firma definida en ambos? Si el niño anula que mi ejemplo anterior NO cambiaría el mismo almacenamiento, ¿tal vez? Esto lleva a una mayor confusión.

Creo que existe una fuerte justificación del espacio informativo para la herencia estática limitada. Más sobre los límites en breve. Este pseudocódigo muestra el valor:

public static class MyStaticBase<T>
{
   public static T Payload;
   public static void Load(StorageSpecs);
   public static void Save(StorageSpecs);
   public static SomeType AttributeBase
   public static SomeType MethodBase(){/*...*/};
}

Entonces obtienes:

public static class MyStaticChild : MyStaticBase<MyChildPlayloadType>
{
   public static SomeType AttributeChild;
   public static SomeType SomeChildMethod(){/*...*/};
   // No need to create the PlayLoad, Load(), and Save().
   // You, 'should' be prevented from creating them, more on this in a sec...
} 

El uso se parece a:

// ...
MyStaticChild.Load(FileNamePath);
MyStaticChild.Save(FileNamePath);
doSomeThing(MyStaticChild.Payload.Attribute);
doSomething(MyStaticChild.AttributeBase);
doSomeThing(MyStaticChild.AttributeChild);
// ...

La persona que crea el niño estático no necesita pensar en el proceso de serialización siempre y cuando comprenda las limitaciones que se pueden colocar en el motor de serialización de la plataforma o el entorno.

Las estadísticas (singletons y otras formas de 'globales') a menudo surgen en torno al almacenamiento de configuración. La herencia estática permitiría que este tipo de asignación de responsabilidad se representara limpiamente en la sintaxis para que coincida con una jerarquía de configuraciones. Sin embargo, como mostré, existe un gran potencial para una ambigüedad masiva si se implementan conceptos básicos de herencia estática.

Creo que la opción de diseño correcta sería permitir la herencia estática con limitaciones específicas:

  1. Sin anulación de nada. El elemento secundario no puede reemplazar los atributos, campos o métodos base, ... La sobrecarga debería estar bien, siempre que haya una diferencia en la firma que permita al compilador clasificar el elemento secundario frente a la base.
  2. Solo permita bases estáticas genéricas, no puede heredar de una base estática no genérica.

Aún puede cambiar la misma tienda a través de una referencia genérica MyStaticBase<ChildPayload>.SomeBaseField. Pero se desanimaría ya que el tipo genérico tendría que especificarse. Si bien la referencia secundaria sería más limpia:MyStaticChild.SomeBaseField .

No soy un escritor compilador, así que no estoy seguro de si me falta algo sobre las dificultades de implementar estas limitaciones en un compilador. Dicho esto, creo firmemente que existe una necesidad de espacio informativo para una herencia estática limitada y la respuesta básica es que no se puede debido a una elección de diseño pobre (o demasiado simple).

Dale Strickler
fuente
0

Las clases estáticas y los miembros de la clase se utilizan para crear datos y funciones a las que se puede acceder sin crear una instancia de la clase. Los miembros de clase estática se pueden usar para separar datos y comportamientos que son independientes de cualquier identidad de objeto: los datos y las funciones no cambian independientemente de lo que le ocurra al objeto. Las clases estáticas se pueden usar cuando no hay datos o comportamiento en la clase que dependa de la identidad del objeto.

Una clase puede declararse estática, lo que indica que contiene solo miembros estáticos. No es posible utilizar la nueva palabra clave para crear instancias de una clase estática. Las clases estáticas se cargan automáticamente mediante .NET Framework Common Language Runtime (CLR) cuando se carga el programa o el espacio de nombres que contiene la clase.

Use una clase estática para contener métodos que no están asociados con un objeto en particular. Por ejemplo, es un requisito común crear un conjunto de métodos que no actúen sobre los datos de la instancia y que no estén asociados a un objeto específico en su código. Podría usar una clase estática para contener esos métodos.

Las siguientes son las características principales de una clase estática:

  1. Solo contienen miembros estáticos.

  2. No pueden ser instanciados.

  3. Están sellados

  4. No pueden contener constructores de instancias (Guía de programación de C #).

Por lo tanto, crear una clase estática es básicamente lo mismo que crear una clase que contenga solo miembros estáticos y un constructor privado. Un constructor privado evita que la clase sea instanciada.

La ventaja de usar una clase estática es que el compilador puede verificar para asegurarse de que no se agreguen accidentalmente miembros de la instancia. El compilador garantizará que no se puedan crear instancias de esta clase.

Las clases estáticas están selladas y, por lo tanto, no se pueden heredar. No pueden heredar de ninguna clase excepto Object. Las clases estáticas no pueden contener un constructor de instancias; sin embargo, pueden tener un constructor estático. Para obtener más información, consulte Constructores estáticos (Guía de programación de C #).

Zona
fuente
-1

Cuando creamos una clase estática que contiene solo los miembros estáticos y un constructor privado. La única razón es que el constructor estático evita que la clase sea instanciada para que no podamos heredar una clase estática. La única forma de acceder al miembro del clase estática utilizando el nombre de la clase en sí. Intentar heredar una clase estática no es una buena idea.

Soumen Chakraborty
fuente