2009-02-23 10 views
27

Sto sviluppando un set di classi che implementano un'interfaccia comune . Un utente della mia biblioteca si aspetta che ognuna di queste classi applichi un determinato insieme di funzioni statiche. C'è comunque che posso decorare queste classi in modo che il compilatore catturi il caso in cui una delle funzioni non è implementata.Esiste un modo per imporre a una classe C# di implementare determinate funzioni statiche?

So che alla fine verrà catturato durante la creazione del codice che consuma. E so anche come aggirare questo problema usando una specie di classe factory.

Semplicemente curioso di sapere se esistono sintassi/attributi per richiedere funzioni statiche su una classe.

Rimosso la parola "interfaccia" per evitare confusione.

+0

Perdonare la mia curiosità, ma non posso fare a meno di chiedermi perché avresti bisogno di una funzione come questa? Cosa hanno i metodi statici con i metodi di istanza ordinaria (non statici)? Nulla mi viene in mente. Hai voglia di condividere un contesto per la tua domanda? – Dan

+2

Non è necessaria un'istanza per chiamare un metodo statico. Quindi sono piacevoli da usare come metodi di fabbrica, ad esempio un metodo de-serialize. – tpower

+0

@Dan: un ulteriore utilizzo sarebbe la possibilità di utilizzare l'interfaccia senza sapere se si sta lavorando su un'istanza o un tipo (o modificandolo in seguito). Pensa a una classe statica che rappresenta una sorta di elenco singleton e che dovrebbe, come lista appropriata, implementare 'IList '. Certo, la soluzione alternativa per archiviare un'istanza di una classe che implementa 'IList ' nella classe statica è fattibile (e sono consapevole che C# al momento per ora lo consente solo in questo modo), ma la possibilità di dichiarare direttamente una classe come staticamente l'implementazione di un'interfaccia eliminerebbe un livello non necessario di riferimento indiretto. –

risposta

28

No, non c'è supporto linguistico per questo in C#. Esistono due soluzioni temporanee che posso pensare immediatamente:

  • utilizzare la riflessione in fase di esecuzione; le dita incrociate e sperare ...
  • utilizzano un Singleton/default-instance/simile ad implementare un'interfaccia che dichiara i metodi

(aggiornamento)

In realtà, finché si dispone di unità -testing, la prima opzione non è poi così male come si potrebbe pensare se (come me) si provenga da un rigido background di "static typing". Il fatto è; funziona bene in linguaggi dinamici. E in effetti, questo è esattamente il modo in cui funziona il mio codice generic operators - speranze si hanno gli operatori statici. In fase di esecuzione, se non lo fai, ti deriderà con un tono adeguatamente beffardo ... ma non può controllare in fase di compilazione.

+11

Mi piacerebbe se il codice ridesse di me in un tono beffardo adatto. Aspetta, succederebbe sempre. Non importa. ;-) –

4

Sfortunatamente no, non c'è niente di simile a questo costruito nella lingua.

0

No, non ci sarebbe alcun punto in questa funzione. Le interfacce sono fondamentalmente una forma ridotta di ereditarietà multipla. Dicono al compilatore come impostare la tabella delle funzioni virtuali in modo che i metodi virtuali non statici possano essere chiamati correttamente nelle classi discendenti. I metodi statici non possono essere virtuali, quindi, non ha senso usare le interfacce per loro.

+2

Ci * sicuramente * è un punto in questo - solo perché i metodi non sarebbero chiamati tramite l'interfaccia non significa che non valga la pena provare che sono lì. È un po 'come il nuovo vincolo T() che C# espone - l'effettiva implementazione della nuova T() passa efficacemente attraverso la riflessione ... –

+0

Non voglio che siano virtuali, basta forzarli ad esistere. – tpower

+0

Sono con dsimcha su questo. Se lo scopo dell'interfaccia dichiarata esplicitamente non è quello di separare l'interfaccia dall'implementazione, allora non rimane molta "interfaccia". Se hai bisogno che alcune classi abbiano dei metodi, allora potresti inventare qualche altro elemento di languge, come HasToImplementMethodsAttrib – Dan

15

No. Fondamentalmente sembra che tu stia cercando una sorta di "polimorfismo statico". Questo non esiste in C#, anche se ho suggerito una sorta di "static interface" notion which could be useful in terms of generics.

Una cosa che si potrebbe fare è scrivere un test unitario semplice per verificare che tutti i tipi in un assembly particolare obbediscono alle regole. Se anche altri sviluppatori implementeranno l'interfaccia, potresti inserire quel codice di test in un posto comune in modo che chiunque implementa l'interfaccia possa facilmente testare i propri assemblaggi.

+0

In un contratto di interfaccia si potrebbe specificare che tutte le implementazioni * legittime * dell'interfaccia devono avere determinati membri statici * e *, se non sigillati, includere nel loro contratto di successione un requisito che tutti i tipi * legittimi * derivati ​​devono rispettare gli stessi requisiti. Ciò non impedirebbe alle persone di scrivere implementazioni illegittime dell'interfaccia, ma la situazione non sarebbe peggiore rispetto a quasi nessun altro tipo di interfaccia. – supercat

+0

@supercat: Per "contratto" intendi solo "documentazione" o qualcosa come Contratti di codice? –

+0

Intendevo documentazione. Qualsiasi interfaccia significativa deve avere una serie di requisiti, in modo tale che le implementazioni che soddisfano tali requisiti siano legittime e le implementazioni che non soddisfano tali requisiti sono illegittime. Non esiste un formato fisso in cui tali requisiti devono essere espressi, ma se ad es. un'implementazione di 'IList ' aveva un setter di proprietà che ignorava il parametro 'index' e sovrascriveva semplicemente un elemento casuale, molti metodi che si aspettavano di funzionare con' IList 'non avrebbero funzionato, non perché i metodi erano difettosi, ma perché il L'implementazione di 'IList ' era illegittima. – supercat

3

Sebbene non vi sia supporto linguistico, è possibile utilizzare uno strumento di analisi statico per applicarlo. Ad esempio, è possibile scrivere una regola personalizzata per FxCop che rileva un attributo o un'implementazione dell'interfaccia su una classe e quindi verifica l'esistenza di determinati metodi statici.

0

L'approccio che ti avvicina a quello che ti serve è un singleton, come suggerito da Marc Gravell.

Le interfacce, tra le altre cose, consentono di fornire un certo livello di astrazione alle classi in modo da poter utilizzare una determinata API indipendentemente dal tipo che la implementa. Tuttavia, poiché è necessario conoscere il tipo di una classe statica per poterlo utilizzare, perché vorresti imporre quella classe per implementare un insieme di funzioni?

Forse potresti utilizzare un attributo personalizzato come [ImplementsXXXInterface] e fornire alcuni controlli di runtime per garantire che le classi con questo attributo implementino effettivamente l'interfaccia necessaria?

+0

Il controllo del runtime non è valido perché verrà già raccolto dal compilatore durante la creazione del codice utente. Mi stavo solo chiedendo se potevo far sì che il compilatore lo riprendesse prima, tutto qui. – tpower

1

Il modello singleton non è utile in tutti i casi. Il mio esempio deriva da un mio progetto reale. Non è artificioso.

Ho una classe (chiamiamola "Widget") che eredita da una classe in un ORM di terze parti. Se istanzio un oggetto Widget (creando quindi una riga nel db) solo per assicurarmi che i miei metodi statici siano dichiarati, sto facendo un casino più grande di quello che sto cercando di ripulire.

Se creo questo oggetto extra in archivio dati, ho avuto modo di nasconderlo da parte degli utenti, calcoli, ecc

Io uso le interfacce in C# per fare in modo che a implementare caratteristiche comuni in un insieme di classi.

Alcuni dei metodi che implementano queste funzionalità richiedono l'esecuzione di dati di istanza. Codigo questi metodi come metodi di istanza e utilizzo un'interfaccia C# per assicurarmi che esistano nella classe.

Alcuni di questi metodi non richiedono dati di istanza, quindi sono metodi statici. Se potessi dichiarare interfacce con metodi statici, il compilatore potrebbe verificare se esistono o meno questi metodi nella classe che dice di implementare l'interfaccia.

5

Questa è una grande domanda e una che ho incontrato nei miei progetti.

Alcune persone sostengono che le interfacce e le classi astratte esistono solo per il polimorfismo, non per forzare i tipi a implementare determinati metodi. Personalmente, considero il polimorfismo un caso d'uso primario e l'implementazione forzata una secondaria. Uso la tecnica di implementazione forzata abbastanza spesso. In genere, viene visualizzato nel codice framework che implementa un modello di modello. La classe base/template racchiude alcune idee complesse e le sottoclassi forniscono numerose varianti implementando i metodi astratti. Un vantaggio pragmatico è che i metodi astratti forniscono una guida ad altri sviluppatori che implementano le sottoclassi. Visual Studio ha persino la possibilità di eliminare i metodi per te. Ciò è particolarmente utile quando uno sviluppatore di manutenzione deve aggiungere una nuova sottoclasse mesi o anni dopo.

Lo svantaggio è che non esiste un supporto specifico per alcuni di questi scenari di modelli in C#. I metodi statici sono uno. Un altro è costruttori; idealmente, ISerializable dovrebbe forzare lo sviluppatore ad implementare il costruttore di serializzazione protetto.

L'approccio più semplice è probabilmente (come suggerito in precedenza) utilizzare un test automatico per verificare che il metodo statico sia implementato sui tipi desiderati. Un'altra idea praticabile già menzionata è l'implementazione di una regola di analisi statica.

Una terza opzione consiste nell'utilizzare un framework di programmazione orientato agli aspetti come PostSharp. PostSharp supporta la convalida degli aspetti in fase di compilazione. È possibile scrivere codice .NET che riflette sull'assembly in fase di compilazione, generando avvisi ed errori arbitrari. Solitamente, lo fai per verificare che un uso dell'aspetto sia appropriato, ma non vedo perché non potresti usarlo per validare anche le regole dei modelli.

0

Se siete solo dopo aver ottenuto questi errori di compilazione, considera questa configurazione:

  1. definire i metodi di un'interfaccia.
  2. Dichiarare i metodi con abstract.
  3. Implementare i metodi statici pubblici e fare in modo che le sovrascritture del metodo astratto richiamino semplicemente i metodi statici.

È un po 'di codice in più, ma saprai quando qualcuno non sta implementando un metodo obbligatorio.

Problemi correlati