Perché non posso ereditare classi statiche?

Ho diverse classi che non hanno davvero bisogno di nessuno stato. Dal punto di vista organizzativo, vorrei metterli in una gerarchia.

Ma sembra che non possa dichiarare l’ereditarietà per le classi statiche.

Qualcosa del genere:

public static class Base { } public static class Inherited : Base { } 

non funzionerà.

Perché i progettisti della lingua hanno chiuso questa possibilità?

Citazione da qui :

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

Ci possono essere motivi per considerare i meccanismi per portare direttamente i membri statici (e lo considereremo in effetti dopo il ciclo del prodotto Orcas), ma l’ereditarietà statica della class non è la strada da percorrere: è il meccanismo sbagliato da utilizzare e funziona solo per membri statici che si trovano in una class statica.

(Mads Torgersen, C # Language PM)

Altre opinioni di channel9

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

I metodi statici vengono mantenuti solo una volta nella 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) all’object su cui viene eseguito il metodo. Questo non succede con i metodi statici (così come sono definiti a livello di testo). Come dovrebbe il compilatore decidere di selezionare il metodo da richiamare?

(littleguru)

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

Il motivo principale per cui non è ansible ereditare una class statica è che sono astratti e sigillati (ciò impedisce anche la creazione di qualsiasi istanza di essi).

Così questo:

 static class Foo { } 

compila a questo IL:

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

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

 MyStaticType.MyStaticMember(); 

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

 MyNewType.MyStaticMember(); 

Pertanto, il nuovo elemento non ha alcuna relazione con l’originale se utilizzato nel codice. Non ci sarebbe modo di sfruttare qualsiasi rapporto di ereditarietà per cose come il polimorfismo.

Forse stai pensando di voler estendere alcuni degli elementi nella class 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 i metodi di estensione.

Forse vuoi essere in grado di passare un Type statico a una funzione in fase di esecuzione e chiamare un metodo su quel tipo, senza sapere esattamente cosa fa il metodo. In tal caso, puoi utilizzare un’interfaccia.

Quindi, alla fine non guadagni nulla dall’ereditarietà delle classi statiche.

Quello che vuoi ottenere usando la gerarchia delle classi può essere ottenuto semplicemente attraverso il namespacing. Quindi i linguaggi che supportano namespapces (come C #) non avranno alcun modo di implementare la gerarchia di classi delle classi statiche. Poiché non è ansible creare un’istanza di una delle classi, tutto ciò che serve è un’organizzazione gerarchica delle definizioni di class che è ansible ottenere tramite l’uso di spazi dei nomi

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

Sebbene sia ansible accedere ai membri statici “ereditati” tramite il nome delle classi ereditate, i membri statici non vengono 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 un Base.Method (), il compilatore mapperà comunque una chiamata a Inherited.Method () di nuovo a Base.Method (). Si potrebbe anche chiamare Base.Method () in modo esplicito. Puoi scrivere un piccolo test e vedere il risultato con Reflector.

Quindi … se non puoi ereditare membri statici e se le classi statiche possono contenere solo membri statici, a cosa serve l’ereditare una class statica?

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

Una soluzione alternativa che puoi fare non è usare le classi statiche ma hide il costruttore in modo che i membri statici delle classi siano l’unica cosa accessibile al di fuori della class. Il risultato è una class “statica” ereditabile essenzialmente:

 public class TestClass { protected TestClass() { } public static T Add(T x, T y) { return (dynamic)x + (dynamic)y; } } public class TestClass : TestClass { // Inherited classs will also need to have protected constructors to prevent people from creating instances of them. protected TestClass() { } } TestClass.Add(3.0, 4.0) TestClass.Add(3, 4) // Creating a class instance is not allowed because the constructors are inaccessible. // new TestClass(); // new TestClass(); 

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

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

Puoi fare qualcosa che assomigli all’eredità statica.

Ecco il trucco:

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

Quindi puoi fare questo:

 public class Base : StaticBase { public Base() { } public void MethodA() { } } public class Inherited : Base { private Inherited() { } public new static void MethodA() { Instance.MethodA(); } } 

La class Inherited non è statica, ma non è consentita la sua creazione. In realtà ha ereditato il costruttore statico che costruisce Base e tutte le proprietà e i metodi di Base disponibili come statici. Ora l’unica cosa che rimane da fare è rendere i wrapper statici per ogni metodo e proprietà che è necessario esporre al contesto statico.

Ci sono aspetti negativi come la necessità di creare manualmente metodi di wrapper statici e new parole chiave. Ma questo approccio aiuta a supportare qualcosa che è davvero simile all’ereditarietà statica.

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

Le classi statiche e i membri della class vengono utilizzati per creare dati e funzioni a cui è ansible accedere senza creare un’istanza della class. I membri di una class statica possono essere utilizzati per separare dati e comportamenti indipendenti da qualsiasi id quadro di object: i dati e le funzioni non cambiano indipendentemente da ciò che accade all’object. Le classi statiche possono essere utilizzate quando non ci sono dati o comportamenti nella class che dipende dall’id quadro dell’object.

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

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

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

  1. Contengono solo membri statici.

  2. Non possono essere istanziati.

  3. Sono sigillati.

  4. Non possono contenere Istance Constructors (C # Programming Guide).

La creazione di una class statica è quindi fondamentalmente la stessa della creazione di una class che contiene solo membri statici e un costruttore privato. Un costruttore privato impedisce l’istanza della class.

Il vantaggio dell’utilizzo di una class statica è che il compilatore può verificare che nessun membro di istanza venga accidentalmente aggiunto. Il compilatore garantirà che non è ansible creare istanze di questa class.

Le classi statiche sono sigillate e quindi non possono essere ereditate. Non possono ereditare da nessuna class ad eccezione di Object. Le classi statiche non possono contenere un costruttore di istanze; tuttavia, possono avere un costruttore statico. Per ulteriori informazioni, consultare Static Constructors (C # Programming Guide).

Quando creiamo una class statica che contiene solo membri statici e un costruttore privato. L’unica ragione è che il costruttore statico impedisce l’istanza della class per il fatto che non possiamo ereditare una class statica. L’unico modo per accedere al membro della class class statica usando il nome della class stessa. Provare ad ereditare una class statica non è una buona idea.