Perché le interfacce C # non possono contenere campi?


223

Ad esempio, supponiamo che io voglia ICarun'interfaccia e che tutte le implementazioni conterranno il campo Year. Questo significa che ogni implementazione deve dichiarare separatamente Year? Non sarebbe meglio definire semplicemente questo nell'interfaccia?


21
Le interfacce non hanno implementazione, per questo uso una classe astratta, con la proprietà Anno
PostMan

6
Per aggiungere ciò che è stato detto qui, le interfacce sono contratti e un campo è un dettaglio di implementazione in quanto definisce uno slot nella memoria della macchina in cui inserire un valore (scalare o puntatore di indirizzo).
Herzmeister,

5
Ma se il campo è pubblico, fa parte del contratto e non solo un dettaglio di implementazione, giusto?
Alex,

Risposte:


238

Sebbene molte delle altre risposte siano corrette a livello semantico, trovo interessante affrontare anche questo tipo di domande dal livello dei dettagli di implementazione.

Un'interfaccia può essere pensata come una raccolta di slot , che contengono metodi . Quando una classe implementa un'interfaccia, la classe è richiesta per indicare al runtime come riempire tutti gli slot richiesti. Quando dici

interface IFoo { void M(); } 
class Foo : IFoo { public void M() { ... } }

la classe dice "quando crei un'istanza di me, inserisci un riferimento a Foo.M nello slot per IFoo.M.

Quindi quando fai una chiamata:

IFoo ifoo = new Foo();
ifoo.M();

il compilatore genera il codice che dice "chiedi all'oggetto quale metodo è nello slot per IFoo.M e chiama quel metodo.

Se un'interfaccia è una raccolta di slot che contengono metodi, alcuni di questi slot possono contenere anche i metodi get e set di una proprietà, i metodi get e set di un indicizzatore e i metodi add e remove di un evento. Ma un campo non è un metodo . Non esiste uno "slot" associato a un campo che è quindi possibile "compilare" con un riferimento alla posizione del campo. Pertanto, le interfacce possono definire metodi, proprietà, indicizzatori ed eventi, ma non campi.


26
L'unica cosa che a volte mi manca è una capacità simile a Java di definire costanti a livello di interfaccia, che presumibilmente non richiederebbe uno "slot" per supportare nella lingua.
LBushkin,

2
Mi piace la spiegazione in parole semplici. Grazie. "CLR via C #" e "Essential .net volume 1" forniscono ulteriori dettagli.
Sandeep GB,

6
Perché un campo non ha uno slot? e stessa domanda con gli operatori? Ricordo di aver sentito parlare della tipizzazione di duck usando la reflection per vedere se un'interfaccia è implementata anche se la classe non ha ereditato l'interfaccia. Perché non è possibile utilizzare la riflessione (o uno slot) per aprire un campo? sto ancora scrivendo il mio codice, quindi potrei non aver bisogno / desiderare campi ma sono stato sorpreso di scoprire che non posso usare gli operatori. gli operatori sono esattamente come i metodi secondo la mia comprensione tranne che non tutti possono essere sovraccaricati ( An interface cannot contain constants, fields, operators. Da msdn.microsoft.com/en-us/library/ms173156.aspx )

@acidzombie: la domanda sul perché un'interfaccia non può definire gli operatori è diversa (anche se forse correlata) al perché non può contenere campi; Suggerirei di pubblicare un'altra domanda se sei ancora interessato a questo.
Adam Robinson,

1
@ b1nary.atr0phy perché con i campi? Ad esempio, se ho dichiarato un metodo int One(), l'implementazione public int One(){return 1;}non è un campo.
Ciao Angelo

131

Le interfacce in C # hanno lo scopo di definire il contratto a cui una classe aderirà, non una particolare implementazione.

In questo spirito, le interfacce C # consentono di definire le proprietà, che il chiamante deve fornire un'implementazione per:

interface ICar
{
    int Year { get; set; }
}

Le classi di implementazione possono utilizzare le proprietà automatiche per semplificare l'implementazione, se non esiste una logica speciale associata alla proprietà:

class Automobile : ICar
{
    public int Year { get; set; } // automatically implemented
}

5
Tutto ciò che è pubblico non fa parte del contratto. Se una classe ha pubblico int Anno, non dice che il contratto di classe ha un campo di tipo Anno per essere presente e accessibile?
Didier A.

1
In ritardo alla festa, ma no, in questo caso significa che il contratto ha un anno di proprietà che ogni classe permanente dovrebbe implementare. Le proprietà sono in realtà metodi get / set, che hanno il campo di supporto generato automaticamente se non è necessaria una logica speciale. La sintassi speciale è solo per una notazione più chiara.
user3613916,

Come posso definire un valore predefinito costante (come 123) per l'implementazione automatica di Year?
lama12345

1
@ lama12345 Sono anche in ritardo alla festa, ma da C # 6 (2015, .NET Framework 4.6 e .NET Core) è possibile utilizzare una proprietà automatica a tale scopo. public int Year => 123;. Tuttavia, in questo caso non ha senso avere un setter, quindi l'interfaccia dovrebbe essere definita conint Year { get; }
EriF89

56

Dichiaralo come una proprietà:

interface ICar {
   int Year { get; set; }
}

47
La domanda è " Perché le interfacce C # non possono contenere campi?". Questo non risolve questo problema.
AakashM,

14
Concordo sul fatto che questa risposta non risponde alla domanda del PO, ma beh, ha risolto il mio problema.
Gorgen,

36

Eric Lippert l'ha inchiodato, userò un modo diverso per dire quello che ha detto. Tutti i membri di un'interfaccia sono virtuali e devono essere sovrascritti da una classe che eredita l'interfaccia. Non scrivi esplicitamente la parola chiave virtuale nella dichiarazione dell'interfaccia, né usi la parola chiave override nella classe, sono impliciti.

La parola chiave virtuale è implementata in .NET con metodi e una cosiddetta v-table, una matrice di puntatori a metodi. La parola chiave override riempie lo slot v-table con un puntatore a metodo diverso, sovrascrivendo quello prodotto dalla classe base. Proprietà, eventi e indicizzatori sono implementati come metodi. Ma i campi non lo sono. Le interfacce non possono quindi contenere campi.


Hai fornito il nome tecnico / effettivo (v-table) al concetto di slot menzionato da Eric. Grazie per il dettaglio Hans.
RBT

Quale sarebbe il punto di una v-table se le interfacce non fossero implementate di default? Questo cambia in C # 8.0, ma non è questo il punto.
AnthonyMonterrosa l'

19

Perché non avere solo una Yearproprietà, che è perfettamente bene?

Le interfacce non contengono campi perché i campi rappresentano un'implementazione specifica della rappresentazione dei dati e la loro esposizione spezzerebbe l'incapsulamento. Quindi avere un'interfaccia con un campo significherebbe effettivamente codificare un'implementazione anziché un'interfaccia, il che è un curioso paradosso per un'interfaccia!

Ad esempio, una parte delle Yearspecifiche potrebbe richiedere che non sia valido per gli ICarimplementatori consentire l'assegnazione a un periodo Yearsuccessivo all'anno in corso + 1 o prima del 1900. Non c'è modo di dire che se avessi Yearcampi esposti - molto meglio da usare proprietà invece di fare il lavoro qui.


18

La risposta breve è sì, ogni tipo di implementazione dovrà creare la propria variabile di supporto. Questo perché un'interfaccia è analoga a un contratto. Tutto ciò che può fare è specificare particolari parti di codice accessibili al pubblico che un tipo di implementazione deve rendere disponibili; non può contenere alcun codice stesso.

Considera questo scenario usando ciò che suggerisci:

public interface InterfaceOne
{
    int myBackingVariable;

    int MyProperty { get { return myBackingVariable; } }
}

public interface InterfaceTwo
{
    int myBackingVariable;

    int MyProperty { get { return myBackingVariable; } }
}

public class MyClass : InterfaceOne, InterfaceTwo { }

Abbiamo un paio di problemi qui:

  • Poiché tutti i membri di un'interfaccia sono - per definizione - pubblici, la nostra variabile di supporto è ora esposta a chiunque utilizzi l'interfaccia
  • Quale myBackingVariablesarà MyClassusare?

L'approccio più comune adottato è dichiarare l'interfaccia e una classe astratta barebone che la implementa. Ciò consente la flessibilità di ereditare dalla classe astratta e ottenere l'implementazione gratuitamente, oppure implementare esplicitamente l'interfaccia e avere la possibilità di ereditare da un'altra classe. Funziona in questo modo:

public interface IMyInterface
{
    int MyProperty { get; set; }
}

public abstract class MyInterfaceBase : IMyInterface
{
    int myProperty;

    public int MyProperty
    {
        get { return myProperty; }
        set { myProperty = value; }
    }
}

7

Altri hanno dato il "Perché", quindi aggiungerò che la tua interfaccia può definire un controllo; se lo avvolgi in una proprietà:

public interface IView {
    Control Year { get; }
}


public Form : IView {
    public Control Year { get { return uxYear; } } //numeric text box or whatever
}

3

Le interfacce non contengono alcuna implementazione.

  1. Definire un'interfaccia con una proprietà.
  2. Inoltre è possibile implementare tale interfaccia in qualsiasi classe e utilizzare questa classe in futuro.
  3. Se necessario, è possibile definire questa proprietà come virtuale nella classe in modo da poterne modificare il comportamento.

3

Molto è già stato detto, ma per semplificare, ecco la mia opinione. Le interfacce hanno lo scopo di avere contratti sui metodi che devono essere implementati dai consumatori o dalle classi e non avere campi per memorizzare valori.

Si può sostenere che quindi perché le proprietà sono consentite? Quindi la semplice risposta è: le proprietà sono definite internamente solo come metodi.


1
Se hai bisogno di accedere a un membro, rendilo una proprietà e sarai bravo.
Unome,

0

Per questo puoi avere una classe base Car che implementa il campo year e tutte le altre implementazioni possono ereditare da esso.


0

Un'interfaccia definisce proprietà e metodi di istanza pubblica . I campi sono in genere privati, o al massimo protetti, interni o interni protetti (il termine "campo" non è in genere utilizzato per nulla di pubblico).

Come indicato da altre risposte, è possibile definire una classe di base e definire una proprietà protetta che sarà accessibile da tutti gli eredi.

Una stranezza è che un'interfaccia può in effetti essere definita come interna ma limita l'utilità dell'interfaccia e viene in genere utilizzata per definire funzionalità interne che non sono utilizzate da altri codici esterni.

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.