Perché i metodi statici non dovrebbero essere sovrascrivibili?

13

Nelle risposte a questo domanda, il consenso generale è che i metodi statici non devono essere sovrascritti (e quindi le funzioni statiche in C # non possono essere virtuali o astratte). Questo non è solo il caso di C #, però; Anche Java lo proibisce e anche il C ++ sembra non piacere. Tuttavia, posso pensare a molti esempi di funzioni statiche che vorrei sovrascrivere in una classe figlia (ad esempio, metodi factory). Mentre in teoria, ci sono modi per aggirarli, nessuno di essi è pulito o semplice.

Perché le funzioni statiche non dovrebbero essere sovrascrivibili?

    
posta Garan 06.09.2016 - 19:01
fonte

5 risposte

14

Con i metodi statici, non esiste alcun oggetto che fornisca il controllo appropriato del meccanismo di override.

Il normale meccanismo del metodo virtuale di classe / istanza consente un controllo finemente ottimizzato delle sostituzioni come segue: ogni oggetto reale è un'istanza di esattamente una classe. Quella classe determina il comportamento degli override; ottiene sempre il primo crack con i metodi virtuali. Può quindi scegliere di chiamare il metodo genitore al momento giusto per la sua implementazione. Quindi, ogni metodo genitore ottiene anche il proprio turno per invocare il suo metodo genitore. Ciò si traduce in una bella cascata di invocazioni dei genitori, che realizza una delle nozioni di riutilizzo del codice per cui è noto l'orientamento dell'oggetto. (Qui il codice di base / super classi viene riutilizzato in un modo relativamente complesso, un'altra nozione ortogonale di riutilizzo del codice in OOP sta semplicemente avendo più oggetti della stessa classe.)

Le classi base possono essere riutilizzate da varie sottoclassi e ciascuna può coesistere comodamente. Ogni classe che viene utilizzata per istanziare oggetti che dettano il proprio comportamento, coesistono pacificamente e simultaneamente con gli altri. Il cliente ha il controllo su quali comportamenti vuole e quando sceglie quale classe utilizzare per creare un'istanza di un oggetto e passare agli altri come desiderato.

(Questo non è un meccanismo perfetto, poiché è sempre possibile identificare le funzionalità che non sono supportate, ovverosia, motivo per cui modelli come il metodo factory e l'iniezione delle dipendenze sono sovrapposti sopra.)

Quindi, se dovessimo fare una funzione di override per la statica senza cambiare altro, avremmo difficoltà ad ordinare gli override. Sarebbe difficile definire un contesto limitato per l'applicabilità dell'override, quindi si otterrebbe l'override a livello globale piuttosto che più localmente come con gli oggetti. Non esiste un oggetto istanza per cambiare il comportamento. Quindi, se qualcuno ha invocato il metodo statico che è stato sovrascritto da un'altra classe, l'override dovrebbe avere il controllo o no? Se ci sono più di questi override, chi prende il controllo per primo? secondo? Con le sovrascritture degli oggetti di esempio, tutte queste domande hanno risposte significative e ben ragionate, ma con la statistica non lo fanno.

Le sostituzioni per la statica sarebbero sostanzialmente caotiche e, cose del genere sono già state fatte prima.

Ad esempio, il sistema Mac OS 7 e precedenti hanno utilizzato un meccanismo di patch trap per estendere il sistema ottenendo il controllo delle chiamate di sistema fatte dall'applicazione prima del sistema operativo. Potresti pensare alla tabella di patch delle chiamate di sistema come a una matrice di puntatori di funzioni, proprio come un vtable per oggetti di istanza, eccetto che era un unico tavolo globale.

Questo ha causato dolore indicibile ai programmatori a causa della natura non ordinata delle patch trap. Alla fine, chi ha toppato la trappola per ultimo ha vinto, anche se non volevano. Ogni patcher del trap catturerebbe il precedente valore trap per una sorta di capacità di chiamata genitore, che era estremamente fragile. Rimuovere una patch trap, dire che non è più necessario conoscere una chiamata di sistema è stata considerata una cattiva forma in quanto non si avevano realmente le informazioni necessarie per rimuovere la patch (se lo facevi, si eliminerebbero anche eventuali altre patch che erano state seguite voi).

Questo non vuol dire che sarebbe impossibile creare un meccanismo per l'override della statica, ma quello che probabilmente preferirei fare invece è trasformare i campi statici e i metodi statici in campi di istanze e metodi di istanza di metaclassi, in modo che si applicherebbero quindi le normali tecniche di orientamento dell'oggetto. Tieni presente che esistono anche sistemi che eseguono questa operazione: CSE 341: Classi Smalltalk e metaclassi ; Vedi anche: Qual è l'equivalente Smalltalk di Java statico?

Sto tentando di dire che dovresti fare un disegno serio delle caratteristiche linguistiche per farlo funzionare anche abbastanza bene. Per un esempio, un approccio ingenuo è stato fatto, è andato zoppicando, ma è stato molto problematico, e probabilmente (vale dire che direi) architettonicamente imperfetto fornendo un'astrazione incompleta e difficile da usare.

Quando hai finito di progettare la funzione di sostituzioni statiche per funzionare correttamente, potresti aver appena inventato una qualche forma di metaclassi, che è un'estensione naturale di OOP in / per i metodi basati su classi. Quindi, non c'è motivo per non farlo - e alcune lingue effettivamente lo fanno. Forse è solo un po 'più di un requisito marginale che un certo numero di lingue sceglie di non fare.

    
risposta data 06.09.2016 - 19:26
fonte
9

L'override dipende dal dispatch virtuale: si utilizza il tipo di runtime del parametro this per decidere quale metodo chiamare. Un metodo statico non ha alcun parametro this , quindi non c'è niente da inviare.

Alcune lingue, in particolare Delphi e Python, hanno un ambito "intermedio" che consente questo: metodi di classe. Un metodo di classe non è un metodo di istanza ordinario, ma non è neanche statico; riceve un parametro self (in modo divertente, entrambi i linguaggi chiamano il this parametro self ) che è un riferimento al tipo di oggetto stesso, piuttosto che a un'istanza di quel tipo. Con quel valore, ora hai a disposizione un tipo di invio virtuale.

Sfortunatamente, né la JVM né il CLR hanno qualcosa di simile.

    
risposta data 06.09.2016 - 19:55
fonte
3

Chiedi

Why shouldn't static functions be overrideable?

Chiedo

Why should functions you wish to override be static?

Alcune lingue ti costringono ad avviare lo spettacolo in un metodo statico. Ma dopo puoi davvero risolvere moltissimi problemi senza alcun metodo più statico.

Ad alcune persone piace usare metodi statici ogni volta che non c'è dipendenza dallo stato in un oggetto. Ad alcune persone piace usare metodi statici per la costruzione di altri oggetti. Ad alcune persone piace evitare il più possibile i metodi statici.

Nessuna di queste persone ha torto.

Se hai bisogno che sia superabile basta smettere di etichettarlo statico. Nove si rompono perché hai un oggetto senza stato che vola in giro.

    
risposta data 06.09.2016 - 20:46
fonte
2

Why shouldn't static methods be able to be overrideable?

Non è una questione di "dovrebbe".

"Sovrascrivere" significa "invia dinamicamente ". "Metodo statico" significa "invio statico". Se qualcosa è statico, non può essere sovrascritto. Se qualcosa può essere ignorato, non è statico.

La tua domanda è piuttosto simile a chiedere: "Perché i tricicli non dovrebbero avere quattro ruote?" La definizione di "triciclo" è quella a tre ruote. Se è un triciclo, non può avere quattro ruote, se ha quattro ruote, non può essere un triciclo. Allo stesso modo, la definizione di "metodo statico" è che è stata inviata staticamente. Se si tratta di un metodo statico, non può essere inviato dinamicamente, se può essere inviato dinamicamente, non può essere un metodo statico.

Ovviamente, è perfettamente possibile avere metodi di classe che possono essere sovrascritti. Oppure, si potrebbe avere un linguaggio come Ruby, in cui le classi sono oggetti proprio come qualsiasi altro oggetto e quindi possono avere metodi di istanza, che elimina completamente la necessità di utilizzare i metodi di classe. (Ruby ha solo un tipo di metodi: metodi di istanza. Non ha metodi di classe, metodi statici, costruttori, funzioni o procedure.)

    
risposta data 07.09.2016 - 16:59
fonte
1

thus static functions in C# cannot be virtual or abstract

In C #, si chiamano sempre membri statici usando la classe, ad esempio BaseClass.StaticMethod() , non baseObject.StaticMethod() . Quindi, se alla fine hai ereditato ChildClass da BaseClass e childObject un'istanza di ChildClass , non sarai in grado di chiamare il tuo metodo statico da childObject . Avrai sempre bisogno di usare esplicitamente la classe reale, quindi un static virtual non ha senso.

Quello che puoi fare è ridefinire lo stesso metodo static nella tua classe figlia e usare la parola chiave new .

class BaseClass {
    public static int StaticMethod() { return 1; }
}

class ChildClass {
    public static new int StaticMethod() { return BaseClass.StaticMethod() + 2; }
}

int result;    
var baseObj = new BaseClass();
var childObj = new ChildClass();

result = BaseClass.StaticMethod();
result = baseObj.StaticMethod(); // DOES NOT COMPILE

result = ChildClass.StaticMethod();
result = childObj.StaticMethod(); // DOES NOT COMPILE

Se fosse possibile chiamare baseObject.StaticMethod() , la tua domanda avrebbe senso.

    
risposta data 07.09.2016 - 07:49
fonte