Classi astratte vs interfacce

Sono un po ‘confuso sull’uso delle classi Abstract in C #. In C ++, ha senso definire un modello che le classi che ereditano la class astratta possono seguire. Ma in C # non Interfaccia ha lo stesso scopo?

È vero che le classi astratte possono avere un’implementazione predefinita che non è fornita da Interfaces. Quindi, se non è necessario includere l’implementazione nella class base, è meglio utilizzare le interfacce?

Mi piace ancora fornire un’implementazione astratta predefinita di un’interfaccia, presupponendo che sia un’interfaccia sostanziale (e ha senso). Non si sa mai quando si potrebbe aggiungere qualcosa all’interfaccia che ha un’implementazione predefinita facile che potrebbe essere inclusa e data “gratuitamente” a chiunque erediti dalla class base astratta.

Questo articolo CodeProject contiene molte informazioni sulla differenza tra i due, inclusa una tabella che confronta e confronta le caratteristiche di ciascuno.

Le interfacce definiscono il contratto tra le classi – i modi in cui le classi si chiamano a vicenda. Una class può implementare più interfacce, ma può ereditare solo da una class astratta.

È vero che le classi astratte possono avere un’implementazione predefinita che non è fornita da Interfaces. Quindi, se non è necessario includere l’implementazione nella class base, è meglio utilizzare le interfacce?

Sì :). Se ha senso implementare alcuni metodi nella class base che saranno comuni a tutte le classi ereditate, dovresti usare una class astratta. Se la class base verrà utilizzata solo per definire un’interfaccia ma non esiste una logica comune tra le classi ereditate, utilizzare un’interfaccia.

Per la tua prima domanda, sì.

Per la tua seconda risposta ti darò alcuni suggerimenti che ho seguito.

  • Utilizza classi e interfacce astratte in combinazione per ottimizzare i tuoi compromessi di progettazione.

Usa una class astratta

  • Quando si crea una libreria di classi che sarà ampiamente distribuita o riutilizzata, in particolare per i client, utilizzare una class astratta preferibilmente a un’interfaccia; perché semplifica il controllo delle versioni.

  • Utilizzare una class astratta per definire una class base comune per una famiglia di tipi.

  • Utilizzare una class astratta per fornire un comportamento predefinito.

  • Sottoclass solo una class base in una gerarchia a cui la class appartiene logicamente.

Usa un’interfaccia

  • Quando si crea un progetto autonomo che può essere modificato a piacimento, utilizzare un’interfaccia preferenziale rispetto a una class astratta; perché offre una maggiore flessibilità di design.

  • Utilizzare le interfacce per introdurre il comportamento polimorfico senza sottoclassi e per modellare l’ereditarietà multipla, consentendo a un tipo specifico di supportare numerosi comportamenti.

  • Utilizzare un’interfaccia per progettare una gerarchia polimorfica per i tipi di valore.

  • Usa un’interfaccia quando un contratto immutabile è realmente inteso.

  • Un’interfaccia ben progettata definisce una gamma di funzionalità molto specifica. Suddividi le interfacce che contengono funzionalità non correlate.

È ansible implementare qualsiasi numero di interfacce, ma è ansible ereditare solo una class. Quindi Classi e Interfacce sono animali molto diversi in C # e non puoi usarli in modo intercambiabile. In C # le classi astratte sono ancora classi, non interfacce.

Le interfacce e le classi astratte servono a diversi obiettivi. Le interfacce vengono utilizzate per dichiarare i contratti per le classi mentre le classi astratte vengono utilizzate per condividere un’implementazione comune.

Se si utilizzano solo classi astratte, le classi non possono ereditare da altre classi poiché C # non supporta l’ereditarietà multipla. Se si utilizzano solo interfacce, le classi non possono condividere codice comune.

 public interface IFoo { void Bar(); } public abstract class FooBase : IFoo { public abstract void Bar() { // Do some stuff usually required for IFoo. } } 

Ora possiamo usare l’interfaccia e l’implementazione di base in varie situazioni.

 public class FooOne : FooBase { public override void Bar() { base.Bar(); // Use base implementation. // Do specialized stuff. } } public class FooTwo : FooBase { public override void Bar() { // Do other specialized stuff. base.Bar(); // Use base implementation. // Do more specialized stuff. } } // This class cannot use the base implementation from FooBase because // of inheriting from OtherClass but it can still implement IFoo. public class FooThree : OtherClass, IFoo { public virtual void Bar() { // Do stuff. } } 

Se non si dispone di alcun codice predefinito / comune, quindi utilizzare un’interfaccia.

Una class astratta può anche fungere da modello, in cui definisce i passaggi di alcuni algoritmi e l’ordine in cui vengono chiamati e le classi derivate forniscono l’implementazione di questi passaggi:

 public abstract class Processor { // this is the only public method // implements the order of the separate steps public void Process() { Step1(); Step2(); //... } // implementation is provided by derived classs protected abstract void Step1(); protected abstract void Step2(); } 

Mentre è vero che una class astratta senza implementazione è equivalente a un’interfaccia, le interfacce e le classi astratte vengono utilizzate per cose diverse.

Le interfacce possono essere utilizzate per il polimorfismo nel senso più generale. Ad esempio, ICollection viene utilizzato per definire l’interfaccia per tutte le raccolte (ce ne sono alcune). Qui sta definendo le operazioni che si desidera eseguire su un certo tipo di tipo. Vi sono molti altri usi (come testabilità, dipendenza, ecc.). Inoltre, le interfacce possono essere mescolate e questo funziona sia concettualmente che tecnicamente.

Le classi astratte hanno più a che fare con il comportamento templateable, dove i metodi virtuali sono un posto per “riempire le lacune”. Ovviamente non è ansible combinare classi astratte (almeno, non in C #).

In C # un grande deterrente per l’uso di classi astratte è che puoi usarne solo uno. Con le interfacce hai il vantaggio di non limitare la class base per l’implementazione. A tal fine, utilizzo sempre un’interfaccia anche se creo una class base astratta per facilitare l’implementazione.

Spesso un altro fastidio delle classi astratte di base è che tendono a fare affidamento sugli argomenti del modello. Ciò può rendere molto difficile l’utilizzo del resto del codice. La risposta facile per questo è fornire un’interfaccia per parlare alla class astratta senza conoscere l’argomento type della class template.

Altri sembrano digitare più velocemente la loro risposta, ma mi permettono di riassumere …

Usa un’interfaccia. Se è necessario condividere l’implementazione, è anche ansible creare una class base astratta che fornisca dettagli di implementazione comuni.

Si noti che con C # 3, è ansible fornire un comportamento predefinito per le interfacce attraverso l’uso di metodi di estensione. Ci sono alcune limitazioni, tuttavia, e le classi astratte hanno ancora il loro posto.

La regola che seguo durante la modellazione è: Classi (abstract incluso) e structs model entities. Comportamento del modello di interfacce. Le entity framework che implementano un’interfaccia possono essere considerate come esibenti i comportamenti che l’interfaccia (contratto) espone.

Questo è suggerito in alcune delle risposte ma non esplicitamente dichiarato.

Il fatto che sia ansible implementare più interfacce e ereditare solo da una class base, come se fossero due facce della stessa medaglia, non è un buon modo per guardarlo.

Non pensare alle interfacce come parte di una gerarchia di oggetti. Di solito sono solo piccole parti di funzionalità (o almeno specifiche se non di piccole dimensioni) che la tua vera gerarchia di oggetti può dichiarare come implementanti. Prendi IDisposable per esempio. Se tu fossi quello che scriveva, ti chiederesti se avrebbe dovuto essere una class astratta o un’interfaccia? Sembra ovvio che in questo caso sono due cose completamente diverse. Voglio essere usa e getta. Pensa a ICloneable e IEnumerable. Puoi implementare quelli della tua class senza dover cercare di far derivare la tua class da alcune classi non correlate come List o Array. Oppure prendi IEnumerator. Semplicemente dà un tipo di vista MoveNext a un object. La mia class può fornire quella funzionalità senza dover essere goffamente derivata da qualche altro tipo di dati sequenziali che non ha nulla a che fare con la mia class.

Preferisco sempre le interfacce fintanto che la class base non ha un’implementazione davvero “heavy duty” che farà risparmiare molto tempo agli implementatori. dato che .net consente solo un’ereditarietà della class base, forzare gli utenti ad ereditare è un enorme limite.

Dovresti sempre preferire la programmazione alle interfacce rispetto alle lezioni concrete.

Se vuoi anche avere un’implementazione predefinita puoi comunque creare una class base che implementa le tue interfacce.