Perché non posso ereditare le classi statiche?


224

Ho diverse classi che non hanno davvero bisogno di alcuno stato. Dal punto di vista organizzativo, vorrei metterli nella gerarchia.

Ma sembra che non posso dichiarare l'eredità per le classi statiche.

Qualcosa del genere:

public static class Base
{
}

public static class Inherited : Base
{
}

non funzionerà.

Perché i progettisti del linguaggio hanno chiuso questa possibilità?


Risposte:


174

Citazione da qui :

Questo è in realtà di progettazione. Non sembra esserci alcuna buona ragione per ereditare una classe statica. Ha membri statici pubblici a cui puoi sempre accedere tramite il nome della classe stessa. Le uniche ragioni che ho visto per ereditare roba statica sono state cattive, come il salvataggio di un paio di caratteri di battitura.

Ci possono essere motivi per considerare i meccanismi per portare i membri statici direttamente nell'ambito (e lo prenderemo in considerazione dopo il ciclo del prodotto Orcas), ma l'ereditarietà delle classi statiche non è la strada da percorrere: è il meccanismo sbagliato da usare e funziona solo per i membri statici che risiedono in una classe statica.

(Mads Torgersen, C # Language PM)

Altre opinioni da channel9

L'ereditarietà in .NET funziona solo su base di istanza. I metodi statici sono definiti a livello di tipo e non a livello di istanza. Ecco perché l'override non funziona con metodi / proprietà / eventi statici ...

I metodi statici vengono mantenuti solo una volta in memoria. Non esiste una tabella virtuale ecc. Creata per loro.

Se invochi un metodo di istanza in .NET, gli dai sempre l'istanza corrente. Questo è nascosto dal runtime .NET, ma succede. Ogni metodo di istanza ha come primo argomento un puntatore (riferimento) sull'oggetto su cui viene eseguito il metodo. Ciò non accade con i metodi statici (in quanto definiti a livello di tipo). Come dovrebbe il compilatore decidere di selezionare il metodo da invocare?

(Littleguru)

E come idea preziosa, littleguru ha una parziale "soluzione" per questo problema: il modello Singleton .


92
Mancanza di immaginazione da parte di Torgersen, davvero. Avevo una buona ragione per voler fare questo :)
Thorarin,

10
Cosa ne pensi di questo? Hai una DLL che non è open source / non hai accesso al suo sorgente, diciamo NUnit. Volete estendere la sua classe Assert per avere un metodo come Throws <> (Azione a) (sì, è anche lì, ma a un certo punto non lo era.) Potreste aggiungere al vostro progetto un Assert: NUnit.Framework.Assert classe e quindi aggiungere un metodo Throws. Problema risolto. È più pulito da usare anche nel codice ... invece di dover inventare un altro nome di classe e ricordare quel nome di classe, basta digitare Assert. e vedere i metodi disponibili.
user420667,

4
@KonradMorawski È impossibile scrivere metodi di estensione per le classi statiche. stackoverflow.com/questions/249222/…
Martin Braun,

2
@modiX Certo. Mi hai frainteso qui. Quello che intendevo dire è che la funzionalità richiesta nello scenario descritto da user420667 potrebbe essere fornita ( in futuro ) con la sola possibilità di consentire metodi di estensione per le classi statiche. Nessuna eredità statica necessaria. Questo è il motivo per cui il suo scenario non mi ha giudicato un'ottima logica per l'introduzione dell'ereditarietà statica. Se C # dovesse essere modificato in questo aspetto, perché optare per una riprogettazione maggiore quando una minore sarebbe altrettanto buona in pratica.
Konrad Morawski,

5
@Learner Non è proprio giusto. In .Net tutto eredita objecte tutti dovrebbero saperlo. Quindi le classi statiche ereditano sempreobject , indipendentemente dal fatto che tu lo specifichi esplicitamente o meno.
RenniePet

71

Il motivo principale per cui non è possibile ereditare una classe statica è che sono astratte e sigillate (ciò impedisce anche che vengano create istanze di esse).

Così questo:

static class Foo { }

compila a questo IL:

.class private abstract auto ansi sealed beforefieldinit Foo
  extends [mscorlib]System.Object
 {
 }

17
Stai dicendo che statico è stato implementato come astratto + sigillato. Vuole sapere perché è stato fatto. Perché è sigillato?
Lucas,

22
Questo non risponde alla domanda a tutto; riafferma semplicemente il problema di cui stava chiedendo.
Igby Largeman,

28
Bene, l'OP avrebbe dovuto chiedere "Perché le classi statiche sono sigillate", non "Perché non posso ereditare dalle classi statiche?", La cui risposta è ovviamente "perché sono sigillate" . Questa risposta ottiene il mio voto.
Alex Budovski,

6
grazie per aver condiviso che le classi statiche vengono compilate automaticamente come classi sigillate - mi chiedevo come / quando ciò accadesse!
Segna il

23
@AlexBudovski: questa risposta è corretta ma inutile come dire a una persona persa "sei di fronte a me" quando ti chiedono "dove sono".
DeepSpace101,

24

Pensaci in questo modo: accedi ai membri statici tramite il nome del tipo, in questo modo:

MyStaticType.MyStaticMember();

Se dovessi ereditare da quella classe, dovresti accedervi tramite il nuovo nome del tipo:

MyNewType.MyStaticMember();

Pertanto, il nuovo elemento non ha relazioni con l'originale quando viene utilizzato nel codice. Non ci sarebbe modo di trarre vantaggio da alcuna relazione ereditaria per cose come il polimorfismo.

Forse stai pensando di voler solo estendere alcuni degli oggetti della classe originale. In tal caso, non c'è nulla che ti impedisca di utilizzare solo un membro dell'originale in un tipo completamente nuovo.

Forse vuoi aggiungere metodi a un tipo statico esistente. Puoi farlo già tramite metodi di estensione.

Forse vuoi essere in grado di passare una funzione statica Typea una funzione in fase di runtime e chiamare un metodo su quel tipo, senza sapere esattamente cosa fa il metodo. In tal caso, è possibile utilizzare un'interfaccia.

Quindi, alla fine, non si guadagna nulla dall'ereditare le classi statiche.


5
Non è possibile aggiungere metodi a un tipo statico esistente tramite un metodo di estensione. Si prega di vedere il mio commento sulla risposta accettata per un esempio di utilizzo desiderato. (Fondamentalmente vorresti semplicemente rinominare ciò che hai chiamato MyNewType come MyStaticType, quindi MyStaticType: OldProject.MyStaticType)
user420667

2
Si potrebbero definire relazioni di ereditarietà con tipi statici e membri statici virtuali in modo tale da SomeClass<T> where T:Foopoter accedere ai membri Fooe, se Tfosse una classe che sostituisce alcuni membri statici virtuali Foo, la classe generica userebbe tali sostituzioni. Probabilmente sarebbe anche possibile definire una convenzione attraverso la quale le lingue potrebbero farlo in modo compatibile con l'attuale CLR (ad esempio una classe con tali membri dovrebbe definire una classe non statica protetta che contiene tali membri di istanza, insieme a un campo statico in possesso di un'istanza di quel tipo).
supercat

Mi sono trovato in un caso in cui credo che ereditare una classe statica sia la cosa giusta da fare, anche se ovviamente puoi accedervi comunque. Ho una classe statica per l'invio di flussi di dati grezzi alla stampante (per parlare con le stampanti di etichette industriali). Ha un sacco di dichiarazioni API Windows. Ora mi ritrovo a scrivere un'altra classe per fare scherzi con le stampanti che richiedono le stesse chiamate API. Combinare? Non bene. Dichiararli due volte? Brutta. Ereditare? Buono, ma non permesso.
Loren Pechtel,

3

Ciò che si desidera ottenere utilizzando la gerarchia di classi può essere raggiunto semplicemente attraverso lo spazio dei nomi. Quindi i linguaggi che supportano namespapces (come C #) non faranno uso dell'implementazione della gerarchia di classi di classi statiche. Poiché non è possibile creare un'istanza di nessuna delle classi, è sufficiente disporre di un'organizzazione gerarchica di definizioni di classe che è possibile ottenere mediante l'utilizzo di spazi dei nomi


Ho un caricatore generico che riceve una classe come tipo. Quella classe ha 5 metodi helper e 2 metodi che restituiscono una stringa (nome e descrizione). Potrei averlo scelto male (penso di sì), ma l'unica soluzione che ho trovato è stata un'istanza della classe nel caricatore generico, per accedere ai metodi ... meh.
ANeves,

L'alternativa sarebbe un dizionario gigante, immagino. Inoltre, quando dici "ciò che vuoi ottenere", dovresti invece dire "a cosa sembri puntare" o qualcosa di simile.
ANeves

3

Puoi usare la composizione invece ... questo ti permetterà di accedere agli oggetti di classe dal tipo statico. Ma non posso ancora implementare interfacce o classi astratte


2

Sebbene sia possibile accedere ai membri statici "ereditati" tramite il nome delle classi ereditate, i membri statici non sono realmente ereditati. Questo è in parte il motivo per cui non possono essere virtuali o astratti e non possono essere ignorati. Nel tuo esempio, se hai dichiarato Base.Method (), il compilatore mapperà comunque una chiamata a Inherited.Method () a Base.Method (). Potresti anche chiamare Base.Method () esplicitamente. Puoi scrivere un piccolo test e vedere il risultato con Reflector.

Quindi ... se non puoi ereditare i membri statici e se le classi statiche possono contenere solo membri statici, a che cosa farebbe ereditare una classe statica?


1

Hmmm ... sarebbe molto diverso se avessi solo classi non statiche piene di metodi statici ..?


2
Questo è ciò che mi è rimasto. Lo faccio in questo modo. E non mi piace.
Utente

L'ho fatto anche in questo modo, ma per qualche motivo non sembra estetico quando sai che non avrai mai un'istanza dell'oggetto. Sono sempre angosciata per dettagli come questo, nonostante il fatto che non ci siano costi materiali.
gdbj,

Nella classe non statica piena di metodi statici non è possibile inserire metodi di estensione :)
Jakub Szułakiewicz

1

Una soluzione alternativa che puoi fare non è usare le classi statiche ma nascondere il costruttore, quindi i membri statici delle classi sono l'unica cosa accessibile al di fuori della classe. Il risultato è essenzialmente una classe "statica" ereditabile:

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>();

Sfortunatamente a causa della limitazione del linguaggio "by-design" non possiamo fare:

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

public static class TestClass : TestClass<double>
{
}

1

Puoi fare qualcosa che sembrerà eredità statica.

Ecco il trucco:

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

Quindi puoi farlo:

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

    public void MethodA()
    {
    }
}

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

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

La Inheritedclasse non è statica in sé, ma non è consentito crearla. In realtà ha ereditato il costruttore statico che costruisce Basee tutte le proprietà e i metodi Basedisponibili come statici. Ora l'unica cosa rimasta da fare è creare wrapper statici per ogni metodo e proprietà che è necessario esporre al proprio contesto statico.

Ci sono aspetti negativi come la necessità di creare manualmente metodi di wrapper statici e newparole chiave. Ma questo approccio aiuta a supportare qualcosa di veramente simile all'eredità statica.

PS L'abbiamo usato per creare query compilate, e questo in realtà può essere sostituito con ConcurrentDictionary, ma un campo di sola lettura statico con la sua sicurezza thread era abbastanza buono.


1

La mia risposta: scarsa scelta progettuale. ;-)

Questo è un dibattito interessante incentrato sull'impatto della sintassi. Il cuore dell'argomento, a mio avviso, è che una decisione di progettazione ha portato a classi statiche sigillate. Un focus sulla trasparenza dei nomi della classe statica che appare al livello più alto invece di nascondersi ("confondere") dietro i nomi dei figli? Si può immaginare un'implementazione del linguaggio che potrebbe accedere direttamente alla base o al bambino, confondendo.

Un pseudo esempio, supponendo che l'ereditarietà statica sia stata definita in qualche modo.

public static class MyStaticBase
{
    SomeType AttributeBase;
}

public static class MyStaticChild : MyStaticBase
{
    SomeType AttributeChild;
}

porterebbe a:

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

quale potrebbe (avrebbe?) influire sulla stessa memoria di

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

Molto confuso!

Ma aspetta! In che modo il compilatore gestirà MyStaticBase e MyStaticChild con la stessa firma definita in entrambi? Se il bambino ha la precedenza sul mio esempio di cui sopra NON cambierebbe la stessa memoria, forse? Questo porta ad ancora più confusione.

Credo che ci sia una forte giustificazione dello spazio informativo per l'ereditarietà statica limitata. Più sui limiti a breve. Questo pseudocodice mostra il valore:

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(){/*...*/};
}

Quindi ottieni:

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...
} 

L'utilizzo è simile a:

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

La persona che crea il figlio statico non deve pensare al processo di serializzazione fintanto che comprende eventuali limitazioni che potrebbero essere poste sul motore di serializzazione della piattaforma o dell'ambiente.

Le statistiche (singleton e altre forme di 'globali') spesso emergono dalla memoria di configurazione. L'ereditarietà statica consentirebbe a questo tipo di allocazione di responsabilità di essere rappresentata in modo chiaro nella sintassi in modo che corrisponda a una gerarchia di configurazioni. Tuttavia, come ho dimostrato, esiste un grande potenziale di ambiguità enorme se vengono implementati concetti di ereditarietà statica di base.

Credo che la scelta giusta per il progetto sarebbe quella di consentire l'ereditarietà statica con limiti specifici:

  1. Nessuna sostituzione di nulla. Il figlio non può sostituire gli attributi, i campi o i metodi di base, ... Il sovraccarico dovrebbe essere ok, purché ci sia una differenza nella firma che consenta al compilatore di sistemare il figlio rispetto alla base.
  2. Consenti solo basi statiche generiche, non puoi ereditare da una base statica non generica.

È comunque possibile modificare lo stesso negozio tramite un riferimento generico MyStaticBase<ChildPayload>.SomeBaseField. Ma saresti scoraggiato poiché il tipo generico dovrebbe essere specificato. Mentre il riferimento bambino sarebbe più pulito: MyStaticChild.SomeBaseField.

Non sono uno scrittore di compilatori, quindi non sono sicuro che mi manchi qualcosa sulle difficoltà di implementare queste limitazioni in un compilatore. Detto questo, sono fermamente convinto che vi sia una necessità di spazio informativo per un'eredità statica limitata e la risposta di base è che non è possibile a causa di una scelta di progettazione scadente (o troppo semplice).


0

Le classi statiche e i membri della classe vengono utilizzati per creare dati e funzioni a cui è possibile accedere senza creare un'istanza della classe. I membri di classe statici possono essere utilizzati per separare dati e comportamenti indipendenti da qualsiasi identità di oggetto: i dati e le funzioni non cambiano indipendentemente da ciò che accade all'oggetto. Le classi statiche possono essere utilizzate quando non vi sono dati o comportamenti nella classe che dipendono dall'identità dell'oggetto.

Una classe può essere dichiarata statica, il che indica che contiene solo membri statici. Non è possibile utilizzare la nuova parola chiave per creare istanze di una classe statica. Le classi statiche vengono caricate automaticamente da Common Language Runtime (CLR) di .NET Framework quando viene caricato il programma o lo spazio dei nomi che contiene la classe.

Utilizzare una classe statica per contenere metodi non associati a un oggetto particolare. Ad esempio, è un requisito comune creare un insieme di metodi che non agiscono sui dati dell'istanza e non sono associati a un oggetto specifico nel codice. È possibile utilizzare una classe statica per contenere tali metodi.

Di seguito sono riportate le caratteristiche principali di una classe statica:

  1. Contengono solo membri statici.

  2. Non possono essere istanziati.

  3. Sono sigillati.

  4. Non possono contenere costruttori di istanze (Guida per programmatori C #).

La creazione di una classe statica è quindi sostanzialmente uguale alla creazione di una classe che contiene solo membri statici e un costruttore privato. Un costruttore privato impedisce alla classe di essere istanziata.

Il vantaggio dell'utilizzo di una classe statica è che il compilatore può verificare per assicurarsi che nessun membro dell'istanza venga aggiunto accidentalmente. Il compilatore garantirà che non è possibile creare istanze di questa classe.

Le classi statiche sono sigillate e quindi non possono essere ereditate. Non possono ereditare da nessuna classe tranne Object. Le classi statiche non possono contenere un costruttore di istanze; tuttavia, possono avere un costruttore statico. Per ulteriori informazioni, consultare Costruttori statici (Guida per programmatori C #).


-1

Quando creiamo una classe statica che contiene solo i membri statici e un costruttore privato. L'unico motivo è che il costruttore statico impedisce che la classe venga istanziata per non poter ereditare una classe statica. L'unico modo per accedere al membro del classe statica utilizzando il nome della classe stessa. Cercare di ereditare una classe statica non è una buona idea.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.