2010-08-18 13 views
21

voglio fare questo:ereditarietà dell'interfaccia: è possibile estendere le proprietà?

interface IBase 
{ 
    string Property1 { get; } 
} 

interface IInherited : IBase 
{ 
    string Property1 { get; set; } 
} 

Quindi IInherited avrebbe il ereditato proprietà Property1 con funzionalità aggiuntive per consentire set.

È possibile? Qual è la sintassi?

MODIFICA: si noti che ho messo la parola "ereditato" in grassetto. Sto chiedendo specificamente di ereditare la proprietà, non nascondendola dietro una nuova.

+3

Penso che qui ci sia confusione tra l'ereditarietà delle classi e l'ereditarietà delle interfacce. Un'interfaccia in realtà non ne eredita un'altra nella stessa era una classe ne eredita un'altra. Tutto il tuo codice dice che IInherited implementa anche IBase. Il codice va bene, solleva solo un avviso del compilatore perché stai cambiando la firma di Property1 nell'interfaccia. Puoi chiarire questo con nuovo. –

+0

Come notato da molti altri, questa sintassi non è possibile, ma credo che dovrebbe essere. Se si utilizza Reflection su Property1, ci sarebbe un metodo Property1_get e un metodo Property1_set, quindi logicamente sembra che sia possibile implementarli separatamente. E la mancanza di essere in grado di farlo sicuramente mi porta a duplicare il codice qualche volta (o sopportare la nuova parola chiave). – cedd

risposta

24

Se il fatto che l'unico modo per farlo è quello di utilizzare la parola chiave new ti dà fastidio, allora secondo me stai pensando di interfacce sbagliate.

Certo, si potrebbe dire che IInherited "eredita da" IBase; ma che cosa significa veramente ? Queste sono interfacce; stabiliscono contratti di codice. Nascondendo la proprietà IBase.Property1 con new string Property1 { get; set; }, non viene visualizzata alcuna funzionalità. Quindi la ragione tradizionale per cui molti sviluppatori considerano il nascondersi come una cosa "cattiva" - che viola il polimorfismo - è irrilevante in questo caso.

Chiediti: cosa fa davvero quando si tratta di interfacce? Forniscono una garanzia di risposta a determinate chiamate di metodo, giusto?

Quindi, date le seguenti due interfacce:

interface IBase 
{ 
    string Property1 { get; } 
} 

interface IInherited : IBase 
{ 
    new string Property1 { set; } 
} 
  1. Se un oggetto implementa IBase, si può leggere la sua proprietà Property1.
  2. Se un oggetto implementa IInherited, è possibile leggere la sua proprietà Property1 (proprio come con un'implementazione IBase) e si può anche scrivere su di esso.

Ancora una volta, non c'è davvero nulla di problematico qui.

+4

Sei corretto, tranne nei casi di implementazione esplicita. Puoi avere diverse implementazioni per ogni versione, quindi è ancora nascosto. Esiste il potenziale che qualcuno che implementa la tua interfaccia possa fare questo, accidentalmente o di proposito, quindi dovresti fare attenzione a come consumi queste interfacce. Se si desidera evitare questo problema e utilizzare ancora l'ereditarietà, utilizzare "nuovo", ma non aggiungere un secondo getter. –

+0

@Merlyn, d'accordo. Rimuovendo la seconda dichiarazione get questo non violerebbe più l'LSP. –

+1

Il tuo codice dichiara due getter * diversi * per la proprietà. In "IInherited" probabilmente vuoi dichiarare solo 'new string Property1 {set; } '. Una classe che implementa "IInherited" può avere una proprietà con un getter e un setter e implementerà entrambe le interfacce. – Timwi

1

Il tuo codice dovrebbe funzionare comunque ... crea solo un avviso di compilatore a causa della proprietà nascosta1. Per cancellare questo marchio avvertimento Property1 in IInherited con il nuovo prefisso

0

È possibile indicare la proprietà con la parola chiave "nuovo", o si può ignorare l'eredità:

public interface IBase 
{ 
    string Property1 { get; } 
} 

public interface IInherited : IBase 
{ 
    new string Property1 { get; set; } 
} 

Oppure:

public interface IBase 
{ 
    string Property1 { get; } 
} 

public interface IInherited 
{ 
    string Property1 { get; set; } 
} 

In entrambi i casi, questo dovrebbe funzionare:

public class SomeClass : IInherited, IBase 
{ 
    public string Property1 
    { 
     get 
     { 
      // ... 
     } 
     set 
     { 
      // ... 
     } 
    } 
} 

si consiglia di riflettere prima di fare una catena di ereditarietà per le tue interfacce, però. Chi vedrà quale interfaccia? Avresti bisogno di trasmettere a IInherited quando hai passato un IBase? Se è così, puoi essere sicuro che puoi fare quel cast (se permetti alle classi create dall'utente, allora la risposta è no)? Questo tipo di eredità può davvero danneggiare (ri) l'usabilità se non stai attento.

1

Non esplicitamente, n. Sono disponibili due opzioni:

public interface IBase 
{ 
    string Property1 { get; } 
} 

public interface IInherited : IBase 
{ 
    void SetProperty1(string value); 
} 

Oppure si può semplicemente uccidere il compilatore di avvertimento con la parola chiave new:

public interface IBase 
{ 
    string Property1 { get; } 
} 

public interface IInherited : IBase 
{ 
    new string Property1 { get; set; } 
} 

A meno che non si sceglie di implementare in modo esplicito IInherited.Property1, IBase si legherà l'implementazione impostabile automaticamente.

+0

Sull'implementazione c'è solo 1 proprietà chiamata Property1. Se si accede all'implementazione tramite un riferimento a IInherited, è possibile chiamare getter e setter e, accedendo tramite IBase, è possibile chiamare solo getter. Ma questo è il comportamento richiesto no? –

+0

Sì, è quello che voglio. Ma probabilmente dovrò andare con i metodi getter/setter, dal momento che sembra che questo non possa essere fatto ... – Malki

+0

Si può fare, esattamente come lo stai facendo ... stai ricevendo un errore del compilatore? Qual è l'errore? –

1

Sfortunatamente no: le proprietà non possono essere estese in quanto tali. Tuttavia, si può solo nascondere la proprietà utilizzando nuova:

interface IInherited : IBase 
{ 
    // The new is actually unnecessary (you get warnings though), hiding is automatic 
    new string Property1 { get; set; } 
} 

In alternativa, è possibile creare il proprio metodi getter e setter che può essere ignorato (buono 'ol stile Java):

interface IBase 
{ 
    string GetProperty1(); 
} 
interface IInherited : IBase 
{ 
    void SetProperty1(string str); 
} 

Proprietà sono effettivamente convertiti in metodi getter e setter dal compilatore.

1

Nascondere un membro sta violando il Principio di sostituzione di Liskov e praticamente non dovrebbe essere fatto, mai. Nascondendo questo membro si sta introducendo un bug molto difficile da localizzare poiché 2 diversi risultati si verificano a seconda che si lanci l'oggetto come IBase1 o lo si lanci su IBase.

http://en.wikipedia.org/wiki/Liskov_substitution_principle

+2

-1. Questo non è nascosto nel senso tradizionale, poiché le interfacce non supportano comunque il polimorfismo. –

+1

Adam: Penso che Chris sia corretto, IFF abbiamo inserito il suo commento nel contesto di interfacce esplicitamente implementate, con diverse implementazioni. –

+2

Una classe che implementa IInherited può essere castata come IBase con minori diritti di modifica, non sovrascrive l'implementazione di un metodo, poiché il getter restituirà lo stesso (poiché le due interfacce 'condividono' l'implementazione getter) –

Problemi correlati