Perché C # non consente ai metodi statici di implementare un'interfaccia?


447

Perché C # è stato progettato in questo modo?

A quanto ho capito, un'interfaccia descrive solo il comportamento e serve allo scopo di descrivere un obbligo contrattuale per le classi che implementano l'interfaccia che un determinato comportamento è implementato.

Se le classi desiderano implementare quel comportamento in un metodo condiviso, perché non dovrebbero?

Ecco un esempio di ciò che ho in mente:

// These items will be displayed in a list on the screen.
public interface IListItem {
  string ScreenName();
  ...
}

public class Animal: IListItem {
    // All animals will be called "Animal".
    public static string ScreenName() {
        return "Animal";
    }
....
}

public class Person: IListItem {

    private string name;

    // All persons will be called by their individual names.
    public string ScreenName() {
        return name;
    }

    ....

 }

6
Bene, Java 8 ce l'ha ( stackoverflow.com/questions/23148471/… ).
liang

1
Scopri come combinare un comportamento statico con l'ereditarietà o l'implementazione dell'interfaccia: stackoverflow.com/a/13567309/880990
Olivier Jacot-Descombes,

1
IListItem.ScreenName() => ScreenName()(usando la sintassi C # 7) implementerà esplicitamente il metodo di interfaccia chiamando il metodo statico. Le cose diventano brutte quando aggiungi l'eredità a ciò, però (devi reimplementare l'interfaccia)
Jeroen Mostert

2
Sto solo facendo sapere a tutti che l'attesa è finita! C # 8.0 ha metodi di interfaccia statica: dotnetfiddle.net/Lrzy6y (anche se funzionano in modo leggermente diverso da come OP voleva che funzionassero - non è necessario implementarli)
Jamie Twells

Risposte:


221

Supponendo che stai chiedendo perché non puoi farlo:

public interface IFoo {
    void Bar();
}

public class Foo: IFoo {
    public static void Bar() {}
}

Questo non ha senso per me, semanticamente. I metodi specificati su un'interfaccia dovrebbero essere lì per specificare il contratto per l'interazione con un oggetto. I metodi statici non ti consentono di interagire con un oggetto: se ti trovi nella posizione in cui l'implementazione potrebbe essere statica, potresti dover chiederti se quel metodo appartiene davvero all'interfaccia.


Per implementare il tuo esempio, darei a Animal una proprietà const, che consentirebbe comunque di accedervi da un contesto statico e restituire quel valore nell'implementazione.

public class Animal: IListItem {
    /* Can be tough to come up with a different, yet meaningful name!
     * A different casing convention, like Java has, would help here.
     */
    public const string AnimalScreenName = "Animal";
    public string ScreenName(){ return AnimalScreenName; }
}

Per una situazione più complicata, puoi sempre dichiarare un altro metodo statico e delegare a quello. Nel provare a fare un esempio, non riuscivo a pensare a nessuna ragione per cui avresti fatto qualcosa di non banale sia in un contesto statico che di istanza, quindi ti risparmierò un blob di FooBar e lo prenderò come un'indicazione che potrebbe non essere una buona idea.


6
Un esempio perfetto! Ma non sono sicuro di aver capito il tuo ragionamento. Sicuramente il compilatore avrebbe potuto essere progettato per esaminare anche i membri statuc? Le istanze non hanno una tabella di indirizzi per l'implementazione di questi metodi? I metodi statici non potrebbero essere inclusi in questa tabella?
Kramii,

12
C'è un caso in cui questo potrebbe essere utile. Ad esempio, desidero che tutti gli implementatori implementino un metodo GetInstance che accetta un argomento XElement. Non posso definirlo come metodo statico nell'interfaccia, né richiedere una firma del costruttore dall'interfaccia.
Oleks

25
Molte persone sono apparse su entrambi i lati: da "Questo non ha senso" a "È un bug, vorrei che tu potessi". (Penso che ci siano casi d'uso validi per questo, ed è così che sono finito qui.) Come soluzione alternativa, il metodo di istanza può semplicemente delegare a un metodo statico.
harpo

5
Potresti anche semplicemente implementare il pezzo come metodo di estensione sull'interfaccia di base, come in: public static object MethodName(this IBaseClass base)all'interno di una classe statica. Il rovescio della medaglia, tuttavia, è che, diversamente dall'ereditarietà dell'interfaccia, ciò non forza / consente ai singoli eredi di scavalcare bene la metodologia.
Troy Alford,

8
Avrebbe molto senso con i generici. Ad esempio void Something <T> () dove T: ISomeInterface {new T (). DoSomething1 (); T.DoSomething2 (); }
Francisco Ryan Tolmasky I

173

La mia ragione tecnica (semplificata) è che i metodi statici non sono presenti nella vtable e che il sito di chiamata è stato scelto al momento della compilazione. È lo stesso motivo per cui non è possibile eseguire l'override o i membri statici virtuali. Per maggiori dettagli, avresti bisogno di un laureato CS o di un compilatore, di cui non sono nessuno.

Per motivi politici, lo farò citerò Eric Lippert (che è un compilatore vincitore, e ha conseguito una laurea in matematica, informatica e matematica applicata presso l'Università di Waterloo (fonte: LinkedIn ):

... il principio progettuale di base dei metodi statici, il principio che dà loro il loro nome ... [è] ... può sempre essere determinato esattamente, al momento della compilazione, quale metodo verrà chiamato. Cioè, il metodo può essere risolto esclusivamente mediante analisi statica del codice.

Nota che Lippert lascia spazio a un cosiddetto metodo di tipo:

Cioè, un metodo associato a un tipo (come un static), che non accetta un argomento "this" non nullable (a differenza di un'istanza o virtuale), ma uno in cui il metodo chiamato dipenderebbe dal tipo costruito di T ( a differenza di uno statico, che deve essere determinabile in fase di compilazione).

ma deve ancora essere convinto della sua utilità.


5
Eccellente, questa è la risposta che volevo scrivere: non conoscevo i dettagli dell'implementazione.
Chris Marasti-Georg,

5
Bella risposta. E voglio questo 'metodo di tipo'! Sarebbe utile in così tante occasioni (pensa ai metadati per un tipo / classe).
Philip Daubmeier,

23
Questa è la risposta corretta Passi un'interfaccia a qualcuno, devono sapere come chiamare un metodo. Un'interfaccia è solo una tabella di metodi virtuali . La tua classe statica non lo possiede. Il chiamante non saprebbe come chiamare un metodo. (Prima di leggere questa risposta ho pensato C # era solo di essere pedante. Ora mi rendo conto che è una limitazione tecnica, imposta da ciò che un'interfaccia è ). Altre persone ti parleranno di come sia un cattivo design. Non è un cattivo design, è una limitazione tecnica.
Ian Boyd

2
+1 per aver effettivamente risposto alla domanda e non essere pedante e arrogante. Come ha detto Ian Boyd: "Non è un cattivo design, è un limite tecnico".
Joshua Pech,

3
È totalmente possibile generare un oggetto per una classe statica con vtable associata. Guarda come Scala gestisce objecti modi e come sono autorizzati a implementare le interfacce.
Sebastian Graf,

97

La maggior parte delle risposte qui sembrano mancare il punto. Il polimorfismo può essere utilizzato non solo tra istanze, ma anche tra tipi. Questo è spesso necessario quando usiamo i generici.

Supponiamo di avere un parametro di tipo nel metodo generico e di dover eseguire alcune operazioni con esso. Non vogliamo istanziare, perché non siamo a conoscenza dei costruttori.

Per esempio:

Repository GetRepository<T>()
{
  //need to call T.IsQueryable, but can't!!!
  //need to call T.RowCount
  //need to call T.DoSomeStaticMath(int param)
}

...
var r = GetRepository<Customer>()

Sfortunatamente, posso trovare solo alternative "brutte":

  • Usa la riflessione Brutta e batte l'idea di interfacce e polimorfismo.

  • Crea una classe di fabbrica completamente separata

    Ciò potrebbe aumentare notevolmente la complessità del codice. Ad esempio, se stiamo cercando di modellare oggetti di dominio, ogni oggetto avrebbe bisogno di un'altra classe di repository.

  • Crea un'istanza e quindi chiama il metodo di interfaccia desiderato

    Questo può essere difficile da implementare anche se controlliamo l'origine per le classi, utilizzate come parametri generici. Il motivo è che, ad esempio, potremmo aver bisogno che le istanze siano solo in ben noti stati "connessi al DB".

Esempio:

public class Customer 
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  void SomeOtherMethod() 
  { 
    //do work...
  }
}

al fine di utilizzare l'istantanea per risolvere il problema dell'interfaccia statica dobbiamo fare quanto segue:

public class Customer: IDoSomeStaticMath
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  //dummy instance
  public Customer() { IsDummy = true; }

  int DoSomeStaticMath(int a) { }

  void SomeOtherMethod() 
  { 
    if(!IsDummy) 
    {
      //do work...
    }
  }
}

Questo è ovviamente brutto e anche inutile complica il codice per tutti gli altri metodi. Ovviamente, neanche una soluzione elegante!


35
+1 per "La maggior parte delle risposte qui sembra mancare l'intero punto."; è incredibile come sembra che praticamente tutte le risposte evitino il nocciolo della domanda per scavare in

5
@Chris Ecco un esempio specifico che mi ha fatto colpire di nuovo questa limitazione. Voglio aggiungere un'interfaccia IResattabile alle classi per indicare che memorizzano nella cache determinati dati in variabili statiche che possono essere ripristinati dall'amministratore del sito (ad esempio un elenco di categorie di ordini, un insieme di tariffe iva, un elenco di categorie recuperate da un'API esterna) per ridurre il DB e gli hit dell'API esterna, questo ovviamente utilizza un reset del metodo statico. Questo mi permette di automatizzare il rilevamento di quali classi possono essere ripristinate. Posso ancora farlo, ma il metodo non viene applicato o aggiunto automaticamente nell'IDE e si basa sulla speranza.
Mattmanser,

6
@Chris Non sono d'accordo, eccessivo ingombro. È sempre un segno di un difetto del linguaggio quando più architettura è la soluzione "migliore". Ricordi tutti gli schemi di cui nessuno parla più da quando C # ha ottenuto generici e metodi anonimi?
Mattmanser,

3
Non puoi usare "dove T: IQueryable, T: IDoSomeStaticMath" o simile?
Roger Willcocks,

2
@ ChrisMarasti-Georg: Un modo un po 'sporco ma interessante per aggirare è questa piccola costruzione:, public abstract class DBObject<T> where T : DBObject<T>, new()e quindi ereditare tutte le classi DB DBObject<T>. Quindi è possibile rendere il recupero da tasto una funzione statica con tipo restituito T nella superclasse astratta, fare in modo che quella funzione crei un nuovo oggetto di T e quindi chiamare un protetto String GetRetrieveByKeyQuery()(definito come astratto sulla superclasse) su quell'oggetto per ottenere la query effettiva da eseguire. Anche se questo potrebbe essere un po 'fuori tema
Nyerguds,

20

So che è una vecchia domanda, ma è interessante. L'esempio non è il migliore. Penso che sarebbe molto più chiaro se mostrassi un caso d'uso:

stringa DoSomething <T> () dove T: ISomeFunction
{
  if (T.someFunction ())
    ...
}

Essere semplicemente in grado di avere metodi statici implementare un'interfaccia non raggiungerebbe ciò che desideri; ciò che sarebbe necessario sarebbe avere membri statici come parte di un'interfaccia. Posso certamente immaginare molti casi d'uso per questo, specialmente quando si tratta di essere in grado di creare cose. Due approcci che potrei offrire che potrebbero essere utili:

  1. Crea una classe generica statica il cui parametro di tipo sarà il tipo che passeresti a DoSomething sopra. Ogni variazione di questa classe avrà uno o più membri statici che contengono elementi correlati a quel tipo. Queste informazioni potrebbero essere fornite facendo in modo che ciascuna classe di interesse chiamasse una routine di "informazioni di registro" o utilizzando Reflection per ottenere le informazioni quando viene eseguito il costruttore statico della variazione di classe. Credo che quest'ultimo approccio sia usato da cose come Comparer <T> .Default ().
  2. Per ogni classe T di interesse, definire una classe o struttura che implementa IGetWhateverClassInfo <T> e soddisfa un "nuovo" vincolo. La classe in realtà non conterrà alcun campo, ma avrà una proprietà statica che restituisce un campo statico con le informazioni sul tipo. Passa il tipo di quella classe o struttura alla routine generica in questione, che sarà in grado di creare un'istanza e usarla per ottenere informazioni sull'altra classe. Se usi una classe per questo scopo, probabilmente dovresti definire una classe generica statica come indicato sopra, per evitare di dover costruire ogni volta una nuova istanza di oggetto descrittore. Se si utilizza una struttura, il costo di istanza dovrebbe essere nullo, ma ogni diverso tipo di struttura richiederebbe una diversa espansione della routine DoSomething.

Nessuno di questi approcci è davvero attraente. D'altra parte, mi aspetterei che se esistessero i meccanismi nel CLR per fornire questo tipo di funzionalità in modo chiaro, .net consentirebbe di specificare vincoli parametrici "nuovi" (dal momento che sapere se una classe ha un costruttore con una firma particolare sembrerebbe essere paragonabile in difficoltà a sapere se ha un metodo statico con una firma particolare).


16

Miopia, immagino.

Quando originariamente progettato, le interfacce erano intese solo per essere utilizzate con istanze di classe

IMyInterface val = GetObjectImplementingIMyInterface();
val.SomeThingDefinedinInterface();

È stato solo con l'introduzione di interfacce poiché i vincoli per i generici hanno avuto un uso pratico aggiungendo un metodo statico a un'interfaccia.

(rispondendo al commento :) Credo che cambiarlo ora richiederebbe una modifica al CLR, il che porterebbe a incompatibilità con assemblee esistenti.


È nel contesto dei generici che ho riscontrato per la prima volta il problema, ma mi chiedo se includere metodi statici nelle interfacce possa essere utile anche in altri contesti? C'è un motivo per cui le cose non possono essere cambiate?
Kramii,

anche io incontro questo durante l'implementazione di una classe generica che richiede il tipo di parametro per crearsi con alcuni parametri. Poiché il nuovo () non può accettarne nessuno. Hai già capito come fare, Kramii?
Tom,

1
@Kramii: contratti per API statiche. Non voglio un'istanza di oggetto, ma solo una garanzia di una firma particolare, ad es. IMatrixMultiplier o ICustomSerializer. Funzionalità / Azioni / Delegati come membri della classe fanno il trucco, ma a volte questo IMO sembra eccessivo e può essere fonte di confusione per gli inesperti che cercano di estendere l'API.
David Cuccia,

14

Le interfacce specificano il comportamento di un oggetto.

I metodi statici non specificano un comportamento di un oggetto, ma un comportamento che influenza in qualche modo un oggetto.


71
Scusa .. Non sono sicuro che sia giusto! Un'interfaccia non specifica il comportamento. Un'interfaccia definisce una serie di operazioni denominate. Due classi potrebbero implementare un metodo di interfaccia per comportarsi in modi completamente diversi. Quindi, un'interfaccia non specifica affatto il comportamento. La classe che lo implementa lo fa.
Scott Langham,

1
Spero che tu non pensi che io sia pignolo ... ma penso che sia una distinzione importante per chiunque impari OO a capire.
Scott Langham,

4
Un'interfaccia dovrebbe specificare un contratto che includa comportamento e presentazione. Ecco perché cambiare il comportamento di una chiamata di interfaccia è un no-no poiché entrambi dovrebbero essere corretti. Se si dispone di un'interfaccia in cui la chiamata agisce in modo diverso (ovvero IList.Add ha rimosso), non sarebbe corretto.
Jeff Yates,

14
Bene, sì, saresti deformato nella testa per definire un metodo per comportarti in modo incongruo al suo nome. Se avessi IAlertService.GetAssistance (), tuttavia, il suo comportamento potrebbe essere quello di far lampeggiare una luce, emettere un allarme o colpire gli occhi con un bastone.
Scott Langham,

1
Un'implementazione sarebbe anche in grado di scrivere in un file di registro. Questo comportamento non è specificato nell'interfaccia. Ma forse hai ragione. Il comportamento di "ottenere assistenza" dovrebbe davvero essere rispettato.
Scott Langham,

14

Nella misura in cui le interfacce rappresentano "contratti", sembra abbastanza ragionevole per le classi statiche implementare interfacce.

Tutti gli argomenti di cui sopra sembrano perdere questo punto sui contratti.


2
Sono totalmente d'accordo con questa risposta semplice ma efficace. Ciò che sarebbe interessante in "interfaccia statica" è che rappresenterebbe un contratto. Forse non dovrebbe essere chiamato "interfaccia statica" ma ci manca ancora un costrutto. Ad esempio, controlla il documento ufficiale di .NET sull'interfaccia ICustomMarshaler. Richiede la classe che lo implementa per "aggiungere un metodo statico chiamato GetInstance che accetta una stringa come parametro e ha un tipo restituito di ICustomMarshaler". Sembra davvero una definizione di "interfaccia statica" in un inglese semplice, mentre la preferirei in C # ...
Simon Mourier,

@SimonMourier Quella documentazione avrebbe potuto essere scritta in modo più chiaro, ma la stai interpretando male. Non è l'interfaccia ICustomMarshaler che richiede il metodo statico GetInstance. È l'attributo del codice [MarshalAs] che lo richiede. Stanno usando il modello factory per consentire all'attributo di ottenere un'istanza del marshaler collegato. Sfortunatamente hanno completamente dimenticato di includere la documentazione del requisito GetInstance nella pagina della documentazione di MarshalAs (mostra solo esempi che utilizzano implementazioni di marshalling integrate).
Scott Gartner,

@ScottGartner - Non capisco. msdn.microsoft.com/en-us/library/… afferma chiaramente: "Oltre all'implementazione dell'interfaccia ICustomMarshaler, i marshaler personalizzati devono implementare un metodo statico chiamato GetInstance che accetta una stringa come parametro e ha un tipo restituito di ICustomMarshaler. il metodo statico viene chiamato dal livello di interoperabilità COM del Common Language Runtime per istanziare un'istanza del marshaler personalizzato. ". Questa è sicuramente una definizione di contratto statico.
Simon Mourier,

9

Poiché lo scopo di un'interfaccia è consentire il polimorfismo, essere in grado di passare un'istanza di qualsiasi numero di classi definite che sono state tutte definite per implementare l'interfaccia definita ... garantendo che all'interno della tua chiamata polimorfica, il codice sarà in grado di trovare il metodo che stai chiamando. non ha senso consentire a un metodo statico di implementare l'interfaccia,

Come lo chiameresti ??


public interface MyInterface { void MyMethod(); }
public class MyClass: MyInterface
{
    public static void MyMethod() { //Do Something; }
}

 // inside of some other class ...  
 // How would you call the method on the interface ???
    MyClass.MyMethod();  // this calls the method normally 
                         // not through the interface...

    // This next fails you can't cast a classname to a different type... 
    // Only instances can be Cast to a different type...
    MyInterface myItf = MyClass as MyInterface;  

1
Altre lingue (Java, ad esempio) consentono di chiamare metodi statici dalle istanze degli oggetti, anche se riceverai un avviso che dovresti chiamarli da un contesto statico.
Chris Marasti-Georg,

Consentire a un metodo statico di essere chiamato da un'istanza è consentito anche in .Net. Questa è una cosa diversa. Se un metodo statico implementasse un'interfaccia, potresti chiamarla SENZA un'istanza. Questo è ciò che non ha senso. Ricorda che non puoi mettere l'implementazione in un'interfaccia, deve essere nella classe. Quindi, se cinque diverse classi fossero definite per implementare quell'interfaccia, e ognuna avesse un'implementazione diversa di questo metodo statico, quale utilizzerebbe il compilatore?
Charles Bretana,

1
@CharlesBretana nel caso dei generici, quello per il tipo passato. Vedo molta utilità nell'avere interfacce per metodi statici (o se vuoi, chiamiamole "interfacce statiche" e permettiamo a una classe di definire entrambe le interfacce statiche e interfacce di istanza). Quindi, se ho, Whatever<T>() where T:IMyStaticInterfacepotrei chiamare Whatever<MyClass>()e avere T.MyStaticMethod()all'interno Whatever<T>()dell'implementazione senza bisogno di un'istanza. Il metodo da chiamare sarebbe determinato in fase di esecuzione. Puoi farlo tramite la riflessione, ma non c'è un "contratto" da forzare.
Jcl

4

Per quanto riguarda i metodi statici utilizzati in contesti non generici, concordo sul fatto che non ha molto senso consentirli nelle interfacce, dal momento che non si sarebbe in grado di chiamarli se si avesse comunque un riferimento all'interfaccia. Tuttavia c'è un buco fondamentale nella progettazione del linguaggio creato usando interfacce NON in un contesto polimorfico, ma in un generico. In questo caso l'interfaccia non è affatto un'interfaccia ma piuttosto un vincolo. Poiché C # non ha alcun concetto di vincolo al di fuori di un'interfaccia, manca una funzionalità sostanziale. Caso in questione:

T SumElements<T>(T initVal, T[] values)
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

Qui non c'è polimorfismo, il generico usa il tipo reale dell'oggetto e chiama l'operatore + =, ma questo non riesce poiché non si può dire con certezza che quell'operatore esiste. La soluzione semplice è specificarlo nel vincolo; la soluzione semplice è impossibile perché gli operatori sono statici e i metodi statici non possono essere in un'interfaccia e (ecco il problema) i vincoli sono rappresentati come interfacce.

Ciò di cui C # ha bisogno è un tipo di vincolo reale, tutte le interfacce sarebbero anche vincoli, ma non tutti i vincoli sarebbero interfacce, quindi potresti farlo:

constraint CHasPlusEquals
{
    static CHasPlusEquals operator + (CHasPlusEquals a, CHasPlusEquals b);
}

T SumElements<T>(T initVal, T[] values) where T : CHasPlusEquals
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

Si è già parlato molto della realizzazione di un IArithmetic da implementare per tutti i tipi numerici, ma c'è preoccupazione per l'efficienza, dal momento che un vincolo non è un costrutto polimorfico, fare un vincolo di CArithmetic risolverebbe quel problema.


Gli operatori in C # sono statici, quindi non ha ancora senso.
John Saunders,

Questo è il punto, il vincolo verifica che il TIPO (non l'istanza) abbia l'operatore + (e per estensione l'operatore + =) e consenta la generazione del modello, quindi quando si verifica l'effettiva sostituzione del modello, l'oggetto è garantito di un tipo che ha quell'operatore (o altro metodo statico.) Quindi puoi dire: SumElements <int> (0, 5); che creerebbe un'istanza: SumElements (int initVal, int [] valori) {foreach (var v in valori) {initVal + = v; }}, che ovviamente ha perfettamente senso
Jeremy Sorensen il

1
Ricorda che non è un modello. Sono generici, che non sono gli stessi dei modelli C ++.
John Saunders,

@JohnSaunders: i generici non sono modelli e non so come potrebbero ragionevolmente essere fatti funzionare con operatori associati staticamente, ma in contesti generici ci sono molti casi in cui potrebbe essere utile specificare che a Tdovrebbe avere un valore statico membro (ad esempio una fabbrica che produce Tistanze). Anche senza cambiamenti di runtime, penso che sarebbe possibile definire convenzioni e metodi di supporto che consentirebbero alle lingue di implementare una cosa come lo zucchero sintattico in modo efficiente e interoperabile. Se per ogni interfaccia con metodi statici virtuali ci fosse una classe helper ...
supercat

1
@JohnSaunders: il problema non è che il metodo implementa un'interfaccia, ma piuttosto che il compilatore non può selezionare un metodo virtuale da chiamare senza avere un'istanza di oggetto su cui basare la selezione. Una soluzione consiste nell'inviare la chiamata "interfaccia statica" non utilizzando l'invio virtuale (che non funzionerà) ma invece utilizzando una chiamata a una classe statica generica. L'invio generico basato sul tipo non richiede di avere un'istanza, ma semplicemente di avere un tipo.
supercat

3

Perché le interfacce hanno una struttura ereditaria e i metodi statici non ereditano bene.


3

Ciò che sembra desiderare consentirebbe di chiamare un metodo statico tramite Type o qualsiasi istanza di quel tipo. Ciò comporterebbe almeno un'ambiguità che non è un tratto desiderabile.

Ci sarebbero infiniti dibattiti sull'importanza, che è la migliore pratica e se ci sono problemi di prestazioni che lo fanno in un modo o nell'altro. Semplicemente non supportandolo C # ci evita di doverci preoccupare.

È anche probabile che un compilatore conforme a questo desiderio perderebbe alcune ottimizzazioni che potrebbero venire con una separazione più stretta tra istanza e metodi statici.


È interessante notare che è del tutto possibile chiamare un metodo statico sia da Type ad Instance in VB.Net (anche se l'IDE dà un avviso in quest'ultimo caso). Non sembra essere un problema. Potresti avere ragione sulle ottimizzazioni.
Kramii,

3

Puoi pensare ai metodi statici e ai metodi non statici di una classe come a diverse interfacce. Quando vengono chiamati, i metodi statici si risolvono nell'oggetto di classe statica singleton e i metodi non statici si risolvono nell'istanza della classe con cui si ha a che fare. Quindi, se usi metodi statici e non statici in un'interfaccia, dichiareresti effettivamente due interfacce quando davvero vogliamo che le interfacce vengano usate per accedere a una cosa coesiva.


Questo è un POV interessante, ed è probabilmente quello che i progettisti di C # avevano in mente. Penserò ai membri statici in un modo diverso da ora in poi.
Kramii,

3

Per fare un esempio in cui mi manca l'implementazione statica dei metodi di interfaccia o quello che Mark Brackett ha introdotto come il "metodo del tipo":

Durante la lettura da un archivio di database, abbiamo una classe DataTable generica che gestisce la lettura da una tabella di qualsiasi struttura. Tutte le informazioni specifiche della tabella vengono inserite in una classe per tabella che contiene anche i dati per una riga dal DB e che deve implementare un'interfaccia IDataRow. In IDataRow è inclusa una descrizione della struttura della tabella da leggere dal database. DataTable deve richiedere la struttura dati da IDataRow prima di leggere dal DB. Attualmente questo sembra:

interface IDataRow {
  string GetDataSTructre();  // How to read data from the DB
  void Read(IDBDataRow);     // How to populate this datarow from DB data
}

public class DataTable<T> : List<T> where T : IDataRow {

  public string GetDataStructure()
    // Desired: Static or Type method:
    // return (T.GetDataStructure());
    // Required: Instantiate a new class:
    return (new T().GetDataStructure());
  }

}

GetDataStructure è richiesto solo una volta per ogni tabella da leggere, l'overhead per l'istanza di un'altra istanza è minimo. Tuttavia, sarebbe bello in questo caso qui.


1

A proposito: potresti ottenere un comportamento simile a quello che desideri creando metodi di estensione per l'interfaccia. Il metodo di estensione sarebbe un comportamento statico condiviso, non sostituibile. Tuttavia, sfortunatamente, questo metodo statico non farebbe parte del contratto.


1

Le interfacce sono insiemi astratti di funzionalità disponibili definite.

Il fatto che un metodo in tale interfaccia si comporti o meno come statico è un dettaglio dell'implementazione che dovrebbe essere nascosto dietro l'interfaccia . Sarebbe sbagliato definire un metodo di interfaccia come statico perché si forzerebbe inutilmente l'implementazione del metodo in un certo modo.

Se i metodi fossero definiti statici, la classe che implementa l'interfaccia non sarebbe incapsulata come potrebbe essere. L'incapsulamento è una buona cosa per cui puntare nella progettazione orientata agli oggetti (non entrerò nel perché, puoi leggerlo qui: http://en.wikipedia.org/wiki/Object-oriented ). Per questo motivo, i metodi statici non sono consentiti nelle interfacce.


1
Sì, una statica nella dichiarazione dell'interfaccia sarebbe sciocca. Una classe non dovrebbe essere costretta a implementare l'interfaccia in un certo modo. Tuttavia, C # limita una classe all'implementazione dell'interfaccia usando metodi non statici. ha senso? Perché?
Kramii,

Non è possibile utilizzare la parola chiave "statico". Non ci sono restrizioni, perché non è necessaria la parola chiave statica per scrivere un metodo che si comporta staticamente. Basta scrivere un metodo che non accede a nessuno dei membri del suo oggetto, quindi si comporterà proprio come un metodo statico. Quindi, c'è una restrizione.
Scott Langham,

Vero Scott, ma non consente a qualcuno di accedere a quel metodo in modo statico, in un'altra parte del codice. Non che io riesca a pensare a una ragione per cui vorresti, ma quello sembra essere ciò che l'OP sta chiedendo.
Chris Marasti-Georg,

Bene, se davvero sentissi la necessità, potresti scriverlo come metodo statico per l'accesso in un'altra parte del codice e scrivere semplicemente il metodo non statico per chiamare il metodo statico. Potrei sbagliarmi, ma dubito che sia un obiettivo dell'interrogante.
Scott Langham,

1

Le classi statiche dovrebbero essere in grado di farlo in modo che possano essere utilizzate genericamente. Ho dovuto invece implementare un Singleton per ottenere i risultati desiderati.

Avevo un gruppo di classi di livello statico di business che implementavano metodi CRUD come "Crea", "Leggi", "Aggiorna", "Elimina" per ogni tipo di entità come "Utente", "Team", ecc. Quindi ho creato una base controllo che aveva una proprietà astratta per la classe Business Layer che implementava i metodi CRUD. Ciò mi ha permesso di automatizzare le operazioni "Crea", "Leggi", "Aggiorna", "Elimina" dalla classe base. Ho dovuto usare un Singleton a causa della limitazione statica.


1

La maggior parte delle persone sembra dimenticare che anche nelle classi OOP ci siano oggetti, e quindi hanno messaggi, che per qualche ragione c # chiama "metodo statico". Il fatto che esistano differenze tra oggetti di istanza e oggetti di classe mostra solo difetti o carenze nel linguaggio. Ottimista su c # però ...


2
Il problema è che questa domanda non riguarda "in OOP". Si tratta di "in C #". In C # non ci sono "messaggi".
John Saunders,

1

OK, ecco un esempio di necessità di un "metodo di tipo". Sto creando una di una serie di classi basate su alcuni XML di origine. Quindi ho un

  static public bool IsHandled(XElement xml)

funzione che viene chiamata a sua volta su ogni classe.

La funzione dovrebbe essere statica poiché altrimenti perdiamo tempo a creare oggetti inappropriati. Come sottolinea @Ian Boyde, potrebbe essere fatto in una classe di fabbrica, ma questo aggiunge solo complessità.

Sarebbe bello aggiungerlo all'interfaccia per forzare gli implementatori di classe a implementarlo. Ciò non causerebbe un notevole sovraccarico: è solo un controllo del tempo di compilazione / collegamento e non influisce sulla vtable.

Tuttavia, sarebbe anche un miglioramento abbastanza lieve. Poiché il metodo è statico, io come chiamante, devo chiamarlo esplicitamente e quindi ottenere un errore di compilazione immediato se non è implementato. Consentire che venga specificato sull'interfaccia significherebbe che questo errore arriva marginalmente prima nel ciclo di sviluppo, ma questo è banale rispetto ad altri problemi dell'interfaccia rotta.

Quindi è una caratteristica potenziale minore che a conti fatti è probabilmente meglio esclusa.


1

Il fatto che una classe statica sia implementata in C # da Microsoft creando un'istanza speciale di una classe con gli elementi statici è solo una stranezza di come si ottiene la funzionalità statica. Non è un punto teorico.

Un'interfaccia DOVREBBE essere un descrittore dell'interfaccia di classe - o come è interagita con, e che dovrebbe includere interazioni statiche. La definizione generale di interfaccia (da Meriam-Webster): il luogo o l'area in cui cose diverse si incontrano e comunicano o si influenzano a vicenda. Quando ometti del tutto i componenti statici di una classe o classi statiche, ignoriamo ampie sezioni di come questi cattivi ragazzi interagiscono.

Ecco un esempio molto chiaro di dove sarebbe utile utilizzare interfacce con classi statiche:

public interface ICrudModel<T, Tk>
{
    Boolean Create(T obj);
    T Retrieve(Tk key);
    Boolean Update(T obj);
    Boolean Delete(T obj);
}

Attualmente, scrivo le classi statiche che contengono questi metodi senza alcun tipo di controllo per assicurarmi di non aver dimenticato nulla. È come ai vecchi tempi della programmazione prima di OOP.


Questa è una domanda antica; al giorno d'oggi, ci sforziamo molto di evitare le domande basate sull'opinione perché sono difficili da rispondere in modo autorevole. L'unico buon modo per rispondere a questo sarebbe quello di descrivere i motivi che la SM aveva, o che avrebbe dovuto dimostrare, per averlo fatto come hanno fatto.
Nathan Tuggy,

1

C # e CLR dovrebbero supportare metodi statici nelle interfacce come fa Java. Il modificatore statico fa parte della definizione di un contratto e ha un significato, in particolare che il comportamento e il valore restituito non variano in base all'istanza sebbene possa comunque variare da chiamata a chiamata.

Detto questo, raccomando che quando si desidera utilizzare un metodo statico in un'interfaccia e non è possibile utilizzare invece un'annotazione. Otterrai la funzionalità che stai cercando.


0

Penso che la risposta breve sia "perché è di utilità zero". Per chiamare un metodo di interfaccia, è necessaria un'istanza del tipo. Dai metodi di istanza puoi chiamare tutti i metodi statici che desideri.


0

Penso che la domanda sia arrivare al fatto che C # ha bisogno di un'altra parola chiave, proprio per questo tipo di situazione. Si desidera un metodo il cui valore restituito dipende solo dal tipo su cui viene chiamato. Non puoi chiamarlo "statico" se detto tipo è sconosciuto. Ma una volta che il tipo diventa noto, diventerà statico. "Statica irrisolta" è l'idea: non è ancora statica, ma una volta che conosciamo il tipo di ricezione, lo sarà. Questo è un concetto perfettamente valido, motivo per cui i programmatori continuano a chiederlo. Ma non si adattava perfettamente al modo in cui i designer pensavano al linguaggio.

Dal momento che non è disponibile, ho iniziato a utilizzare metodi non statici nel modo mostrato di seguito. Non esattamente l'ideale, ma non riesco a vedere alcun approccio che abbia più senso, almeno non per me.

public interface IZeroWrapper<TNumber> {
  TNumber Zero {get;}
}

public class DoubleWrapper: IZeroWrapper<double> {
  public double Zero { get { return 0; } }
}

0

Secondo il concetto orientato agli oggetti Interfaccia implementata da classi e hanno contratto per accedere a queste funzioni implementate (o metodi) usando l'oggetto.

Quindi, se si desidera accedere ai metodi del contratto di interfaccia, è necessario creare l'oggetto. È sempre necessario che non sia consentito in caso di metodi statici. Le classi, i metodi e le variabili statici non richiedono mai oggetti e si caricano in memoria senza creare oggetti di quell'area (o classe) o si può dire che non richiedono la creazione di oggetti.


0

Concettualmente non c'è motivo per cui un'interfaccia non possa definire un contratto che includa metodi statici.

Per l'attuale implementazione del linguaggio C #, la restrizione è dovuta alla tolleranza di eredità di una classe base e delle interfacce. Se "class SomeBaseClass" implementa "interfaccia ISomeInterface" e "class SomeDerivedClass: SomeBaseClass, ISomeInterface" implementa anche l'interfaccia, un metodo statico per implementare un metodo di interfaccia non verrebbe compilato perché un metodo statico non può avere la stessa firma di un metodo di istanza (che sarebbe essere presente nella classe base per implementare l'interfaccia).

Una classe statica è funzionalmente identica a un singleton e ha lo stesso scopo di un singleton con sintassi più pulita. Poiché un singleton può implementare un'interfaccia, le implementazioni dell'interfaccia per statica sono concettualmente valide.

Quindi si riduce semplicemente alla limitazione del conflitto di nomi C # per esempio e ai metodi statici con lo stesso nome attraverso l'ereditarietà. Non vi è alcun motivo per cui C # non possa essere "aggiornato" per supportare contratti con metodi statici (interfacce).


-1

Quando una classe implementa un'interfaccia, crea un'istanza per i membri dell'interfaccia. Mentre un tipo statico non ha un'istanza, non ha senso avere firme statiche in un'interfaccia.

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.