Qual è la differenza tra una funzione astratta e una funzione virtuale? In quali casi si consiglia di utilizzare virtuale o astratto? Qual è l'approccio migliore?
Qual è la differenza tra una funzione astratta e una funzione virtuale? In quali casi si consiglia di utilizzare virtuale o astratto? Qual è l'approccio migliore?
Risposte:
Una funzione astratta non può avere funzionalità. In pratica stai dicendo che qualsiasi classe figlio DEVE fornire la propria versione di questo metodo, tuttavia è troppo generale persino tentare di implementare nella classe genitore.
Una funzione virtuale , in pratica, dice look, ecco la funzionalità che può o non può essere abbastanza buona per la classe child. Quindi, se è abbastanza buono, utilizzare questo metodo, in caso contrario, quindi ignorare me e fornire la propria funzionalità.
Una funzione astratta non ha implementazione e può essere dichiarata solo su una classe astratta. Ciò forza la classe derivata a fornire un'implementazione.
Una funzione virtuale fornisce un'implementazione predefinita e può esistere in una classe astratta o in una classe non astratta.
Quindi per esempio:
public abstract class myBase
{
//If you derive from this class you must implement this method. notice we have no method body here either
public abstract void YouMustImplement();
//If you derive from this class you can change the behavior but are not required to
public virtual void YouCanOverride()
{
}
}
public class MyBase
{
//This will not compile because you cannot have an abstract method in a non-abstract class
public abstract void YouMustImplement();
}
MyBase
classe non deve implementare la classe astratta , in qualche modo? Non lo faccio spesso, quindi potrei sbagliarmi. Non lo vedo nel tuo esempio.
abstract
classi possono avere abstract
membri.abstract
classe che eredita da una abstract
classe deve i override
suoi abstract
membri.abstract
membro è implicitamente virtual
.abstract
membro non può fornire alcuna implementazione ( abstract
viene chiamato pure virtual
in alcune lingue).virtual
o no virtual
. Un abstract
membro (ovvero proprietà astratta, metodo astratto) è proprio come un metodo virtuale, ovvero è possibile sovrascriverlo, tranne per il fatto che non porta con sé un'implementazione predefinita.
Devi sempre sostituire una funzione astratta.
Così:
Funzione astratta:
Funzione virtuale:
Metodo astratto: quando una classe contiene un metodo astratto, tale classe deve essere dichiarata come astratta. Il metodo astratto non ha implementazione e quindi, le classi che derivano da quella classe astratta, devono fornire un'implementazione per questo metodo astratto.
Metodo virtuale: una classe può avere un metodo virtuale. Il metodo virtuale ha un'implementazione. Quando si eredita da una classe che ha un metodo virtuale, è possibile sovrascrivere il metodo virtuale e fornire ulteriore logica o sostituire la logica con la propria implementazione.
Quando usare cosa: in alcuni casi, sai che alcuni tipi dovrebbero avere un metodo specifico, ma non sai quale implementazione dovrebbe avere questo metodo.
In tali casi, è possibile creare un'interfaccia che contiene un metodo con questa firma. Tuttavia, se hai un caso del genere, ma sai che gli implementatori di tale interfaccia avranno anche un altro metodo comune (per il quale puoi già fornire l'implementazione), puoi creare una classe astratta. Questa classe astratta contiene quindi il metodo astratto (che deve essere sostituito) e un altro metodo che contiene la logica "comune".
È necessario utilizzare un metodo virtuale se si dispone di una classe che può essere utilizzata direttamente, ma per la quale si desidera che gli ereditari siano in grado di modificare determinati comportamenti, sebbene non sia obbligatorio.
spiegazione: con analogie. speriamo che ti possa aiutare.
Contesto
Lavoro al 21 ° piano di un edificio. E sono paranoico sul fuoco. Ogni tanto, da qualche parte nel mondo, un fuoco sta bruciando un raschietto del cielo. Ma per fortuna abbiamo un manuale di istruzioni da qualche parte qui su cosa fare in caso di incendio:
Scala di sicurezza()
Questo è fondamentalmente un metodo virtuale chiamato FireEscape ()
Metodo virtuale
Questo piano è abbastanza buono per il 99% delle circostanze. È un piano di base che funziona. Ma c'è una probabilità dell'1% che la scala antincendio sia bloccata o danneggiata, nel qual caso sei completamente fregato e diventerai brindisi a meno che tu non faccia qualche azione drastica. Con i metodi virtuali puoi fare proprio questo: puoi sovrascrivere il piano FireEscape () di base con la tua versione del piano:
In altre parole, i metodi virtuali forniscono un piano di base, che può essere sostituito se necessario . Le sottoclassi possono sovrascrivere il metodo virtuale della classe genitore se il programmatore lo ritiene appropriato.
Metodi astratti
Non tutte le organizzazioni sono ben addestrate. Alcune organizzazioni non eseguono esercitazioni antincendio. Non hanno una politica generale di fuga. Ogni uomo è per se stesso. I dirigenti sono interessati solo a tale politica esistente.
In altre parole, ogni persona è costretta a sviluppare il proprio metodo FireEscape (). Un ragazzo uscirà dalla scala antincendio. Un altro ragazzo paracaduterà. Un altro ragazzo utilizzerà la tecnologia di propulsione a razzo per volare via dall'edificio. Un altro ragazzo si discuterà. La direzione non si preoccupa di come fuggi, purché tu abbia un piano FireEscape () di base - se non lo fai puoi essere certo che OHS scenderà sull'organizzazione come una tonnellata di mattoni. Questo è ciò che si intende con un metodo astratto.
Qual è la differenza tra i due di nuovo?
Metodo astratto: le sottoclassi sono costrette a implementare il proprio metodo FireEscape. Con un metodo virtuale, hai un piano di base che ti aspetta, ma puoi scegliere di implementare il tuo se non è abbastanza buono.
Ora non è stato così difficile, vero?
Un metodo astratto è un metodo che deve essere implementato per creare una classe concreta. La dichiarazione è nella classe astratta (e qualsiasi classe con un metodo astratto deve essere una classe astratta) e deve essere implementata in una classe concreta.
Un metodo virtuale è un metodo che può essere sovrascritto in una classe derivata usando l'override, sostituendo il comportamento nella superclasse. Se non si ignora, si ottiene il comportamento originale. Se lo fai, ottieni sempre il nuovo comportamento. Questo al contrario di metodi non virtuali, che non possono essere ignorati ma possono nascondere il metodo originale. Questo viene fatto usando il new
modificatore.
Vedi il seguente esempio:
public class BaseClass
{
public void SayHello()
{
Console.WriteLine("Hello");
}
public virtual void SayGoodbye()
{
Console.WriteLine("Goodbye");
}
public void HelloGoodbye()
{
this.SayHello();
this.SayGoodbye();
}
}
public class DerivedClass : BaseClass
{
public new void SayHello()
{
Console.WriteLine("Hi There");
}
public override void SayGoodbye()
{
Console.WriteLine("See you later");
}
}
Quando creo un'istanza DerivedClass
e chiamo SayHello
, oppure SayGoodbye
ottengo "Ciao" e "Ci vediamo più tardi". Se chiamo HelloGoodbye
, ricevo "Ciao" e "Ci vediamo più tardi". Questo perché SayGoodbye
è virtuale e può essere sostituito da classi derivate. SayHello
è nascosto, quindi quando lo chiamo dalla mia classe base ottengo il mio metodo originale.
I metodi astratti sono implicitamente virtuali. Definiscono il comportamento che deve essere presente, più come fa un'interfaccia.
I metodi astratti sono sempre virtuali. Non possono avere un'implementazione.
Questa è la differenza principale.
Fondamentalmente, useresti un metodo virtuale se ne hai l'implementazione 'predefinita' e vuoi consentire ai discendenti di cambiarne il comportamento.
Con un metodo astratto, costringi i discendenti a fornire un'implementazione.
L'ho reso più semplice apportando alcuni miglioramenti alle seguenti classi (da altre risposte):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestOO
{
class Program
{
static void Main(string[] args)
{
BaseClass _base = new BaseClass();
Console.WriteLine("Calling virtual method directly");
_base.SayHello();
Console.WriteLine("Calling single method directly");
_base.SayGoodbye();
DerivedClass _derived = new DerivedClass();
Console.WriteLine("Calling new method from derived class");
_derived.SayHello();
Console.WriteLine("Calling overrided method from derived class");
_derived.SayGoodbye();
DerivedClass2 _derived2 = new DerivedClass2();
Console.WriteLine("Calling new method from derived2 class");
_derived2.SayHello();
Console.WriteLine("Calling overrided method from derived2 class");
_derived2.SayGoodbye();
Console.ReadLine();
}
}
public class BaseClass
{
public void SayHello()
{
Console.WriteLine("Hello\n");
}
public virtual void SayGoodbye()
{
Console.WriteLine("Goodbye\n");
}
public void HelloGoodbye()
{
this.SayHello();
this.SayGoodbye();
}
}
public abstract class AbstractClass
{
public void SayHello()
{
Console.WriteLine("Hello\n");
}
//public virtual void SayGoodbye()
//{
// Console.WriteLine("Goodbye\n");
//}
public abstract void SayGoodbye();
}
public class DerivedClass : BaseClass
{
public new void SayHello()
{
Console.WriteLine("Hi There");
}
public override void SayGoodbye()
{
Console.WriteLine("See you later");
}
}
public class DerivedClass2 : AbstractClass
{
public new void SayHello()
{
Console.WriteLine("Hi There");
}
// We should use the override keyword with abstract types
//public new void SayGoodbye()
//{
// Console.WriteLine("See you later2");
//}
public override void SayGoodbye()
{
Console.WriteLine("See you later");
}
}
}
Il binding è il processo di associazione di un nome a un'unità di codice.
L'associazione tardiva significa che usiamo il nome, ma rinviamo la mappatura. In altre parole, creiamo / menzioniamo prima il nome e lasciamo che qualche processo successivo gestisca la mappatura del codice con quel nome.
Ora considera:
Quindi, la risposta breve è: virtual
è un'istruzione di associazione tardiva per la macchina (runtime) mentre abstract
è l'istruzione di associazione tardiva per l'essere umano (programmatore)
In altre parole, virtual
significa:
"Caro runtime , associa il codice appropriato a questo nome facendo ciò che sai fare meglio: la ricerca "
Considerando che abstract
significa:
"Caro programmatore , associa il codice appropriato a questo nome facendo ciò che sai fare meglio: inventare "
Per completezza, sovraccarico significa:
"Caro compilatore , associa il codice appropriato a questo nome facendo quello che sai fare meglio: ordinamento ".
Metodo virtuale :
Virtuale significa che POSSIAMO scavalcarlo.
La funzione virtuale ha un'implementazione. Quando ereditiamo la classe, possiamo ignorare la funzione virtuale e fornire la nostra logica.
Metodo astratto
L'astratto significa che DEVE sostituirlo.
Una funzione astratta non ha implementazione e deve essere in una classe astratta.
Può solo essere dichiarato. Questo costringe la classe derivata a fornirne l'implementazione.
Un membro astratto è implicitamente virtuale. L'abstract può essere definito puro virtuale in alcune lingue.
public abstract class BaseClass
{
protected abstract void xAbstractMethod();
public virtual void xVirtualMethod()
{
var x = 3 + 4;
}
}
Ho visto in alcuni punti il metodo astratto è definito come di seguito. **
"Un metodo astratto deve essere implementato nella classe figlio"
** Ho pensato che fosse così.
Non è necessario implementare un metodo astratto in una classe figlio, se anche la classe figlio è astratta .
1) Un metodo astratto non può essere un metodo privato. 2) Un metodo astratto non può essere implementato nella stessa classe astratta.
Direi .. se stiamo implementando una classe astratta, devi sovrascrivere i metodi astratti dalla classe astratta di base. Perché .. L'implementazione del metodo astratto è con l' override della parola chiave. Simile al metodo virtuale.
Non è necessario implementare un metodo virtuale in una classe ereditata.
----------CODE--------------
public abstract class BaseClass
{
public int MyProperty { get; set; }
protected abstract void MyAbstractMethod();
public virtual void MyVirtualMethod()
{
var x = 3 + 4;
}
}
public abstract class myClassA : BaseClass
{
public int MyProperty { get; set; }
//not necessary to implement an abstract method if the child class is also abstract.
protected override void MyAbstractMethod()
{
throw new NotImplementedException();
}
}
public class myClassB : BaseClass
{
public int MyProperty { get; set; }
//You must have to implement the abstract method since this class is not an abstract class.
protected override void MyAbstractMethod()
{
throw new NotImplementedException();
}
}
La maggior parte degli esempi precedenti usa il codice e sono molto buoni. Non ho bisogno di aggiungere a quello che dicono, ma la seguente è una semplice spiegazione che fa uso di analogie piuttosto che termini di codice / tecnici.
Spiegazione semplice - Spiegazione mediante analogie
Metodo astratto
Pensa a George W. Bush. Dice ai suoi soldati: "Vai a combattere in Iraq". E questo è tutto. Tutto quello che ha specificato è che bisogna combattere. Non specifica come esattamente ciò accadrà. Voglio dire, non puoi semplicemente uscire e "combattere": cosa significa esattamente? combatto con un B-52 o il mio derringer? Quei dettagli specifici sono lasciati a qualcun altro. Questo è un metodo astratto.
Metodo virtuale
David Petraeus è in alto nell'esercito. Ha definito cosa significa combattere:
Il problema è che si tratta di un metodo molto generale. È un buon metodo che funziona, ma a volte non è abbastanza specifico. La cosa buona per Petraeus è che i suoi ordini hanno margine di manovra e portata - ha permesso ad altri di cambiare la sua definizione di "lotta", in base alle loro esigenze particolari.
Private Job Bloggs legge l'ordine di Petraeus e gli viene dato il permesso di implementare la sua versione di combattimento, secondo i suoi requisiti particolari:
Anche Nouri al Maliki riceve gli stessi ordini da Petraeus. Anche lui deve combattere. Ma è un politico, non un uomo di fanteria. Ovviamente non può andare in giro a sparare in testa ai suoi nemici politici. Poiché Petraeus gli ha fornito un metodo virtuale, Maliki può implementare la sua versione del metodo di combattimento, in base alle sue circostanze particolari:
In altre parole, un metodo virtuale fornisce istruzioni sulla piastra di comando, ma queste sono istruzioni generali, che possono essere rese più specifiche dalle persone dell'erarchia dell'esercito, in base alle loro particolari circostanze.
La differenza tra i due
George Bush non dimostra alcun dettaglio di implementazione. Questo deve essere fornito da qualcun altro. Questo è un metodo astratto.
Petraeus d'altra parte non fornire i dettagli di implementazione ma ha dato il permesso per i suoi subordinati di ignorare i suoi ordini con la propria versione, se possono venire con qualcosa di meglio.
spero che aiuti.
Funzione astratta (metodo):
● Un metodo astratto è un metodo dichiarato con la parola chiave abstract.
● Non ha corpo.
● Dovrebbe essere implementato dalla classe derivata.
● Se un metodo è astratto, la classe dovrebbe essere astratta.
funzione virtuale (metodo):
● Un metodo virtuale è il metodo dichiarato con la parola chiave virtual e può essere sostituito dal metodo della classe derivata utilizzando la parola chiave override.
● Spetta alla classe derivata se sostituirla o meno.
La risposta è stata fornita più volte, ma la domanda su quando utilizzarle è una decisione in fase di progettazione. Vorrei che fosse una buona pratica cercare di raggruppare definizioni di metodi comuni in interfacce distinte e inserirle in classi a livelli di astrazione appropriati. Il dump di un insieme comune di definizioni di metodi astratti e virtuali in una classe rende la classe non distinguibile quando potrebbe essere meglio definire una classe non astratta che implementa un insieme di interfacce concise. Come sempre, dipende da ciò che meglio si adatta alle esigenze specifiche delle applicazioni.
La funzione astratta non può avere un corpo e DEVE essere sovrascritta da classi secondarie
La funzione virtuale avrà un corpo e potrebbe o meno essere sovrascritta da classi secondarie
Da una visione generale orientata agli oggetti:
riguardo al metodo astratto : quando metti un metodo astratto nella classe genitrice, in realtà stai dicendo alle classi figlie: Ehi nota che hai una firma del metodo come questa. E se vuoi usarlo dovresti implementare il tuo!
Riguardo alla funzione virtuale : quando metti un metodo virtuale nella classe genitore stai dicendo alle classi derivate: Ehi, qui c'è una funzionalità che fa qualcosa per te. Se questo è utile per te, basta usarlo. In caso contrario, sovrascrivi questo e implementa il tuo codice, anche tu puoi usare la mia implementazione nel tuo codice!
questa è una filosofia sulla differenza tra questi due concetti in General OO
Una funzione astratta è "solo" una firma, senza un'implementazione. Viene utilizzato in un'interfaccia per dichiarare come è possibile utilizzare la classe. Deve essere implementato in una delle classi derivate.
La funzione virtuale (metodo attualmente), è anche una funzione dichiarata e deve essere implementata in una delle classi della gerarchia dell'ereditarietà.
Le istanze ereditate di tale classe ereditano anche l'implementazione, a meno che non venga implementata, in una classe gerarchica inferiore.
Non c'è niente che chiama classe virtuale in C #.
Per le funzioni
Puoi decidere con le tue esigenze.
Il metodo astratto non ha un'implementazione, è dichiarato nella classe genitore. La classe figlio è resposible per l'implementazione di quel metodo.
Il metodo virtuale dovrebbe avere un'implementazione nella classe genitore e ciò facilita la classe figlio a fare la scelta se usare quell'implementazione della classe genitore o se avere una nuova implementazione per se stessa per quel metodo nella classe figlio.
Una funzione o metodo astratto è un "nome dell'operazione" pubblico esposto da una classe, il suo scopo, insieme alle classi astratte, è principalmente quello di fornire una forma di vincolo nella progettazione degli oggetti rispetto alla struttura che un oggetto deve implementare.
In effetti le classi che ereditano dalla sua classe astratta devono dare un'implementazione a questo metodo, generalmente i compilatori generano errori quando non lo fanno.
L'uso di classi e metodi astratti è importante soprattutto per evitare che, concentrandosi sui dettagli dell'implementazione durante la progettazione delle classi, la struttura delle classi sia troppo correlata alle implementazioni, creando così dipendenze e accoppiamento tra classi che collaborano tra loro.
Una funzione o un metodo virtuale è semplicemente un metodo che modella un comportamento pubblico di una classe, ma che possiamo lasciare liberi di modificarlo nella catena dell'ereditarietà, poiché riteniamo che le classi figlio potrebbero dover implementare alcune estensioni specifiche per quel comportamento.
Entrambi rappresentano una forma di polimorfismo nel paradigma dell'orientamento agli oggetti.
Possiamo usare insieme metodi astratti e funzioni virtuali per supportare un buon modello di ereditarietà.
Progettiamo una buona struttura astratta degli oggetti principali della nostra soluzione, quindi creiamo implementazioni di base individuando quelle più inclini a ulteriori specializzazioni e le trasformiamo in virtuali, infine specializziamo le nostre implementazioni di base, eventualmente "ignorando" quelle virtuali ereditate.
Qui sto scrivendo un codice di esempio sperando che questo possa essere un esempio piuttosto tangibile per vedere i comportamenti delle interfacce, delle classi astratte e delle classi ordinarie a un livello molto semplice. Puoi anche trovare questo codice in github come progetto se vuoi usarlo come demo: https://github.com/usavas/JavaAbstractAndInterfaceDemo
public interface ExampleInterface {
// public void MethodBodyInInterfaceNotPossible(){
// }
void MethodInInterface();
}
public abstract class AbstractClass {
public abstract void AbstractMethod();
// public abstract void AbstractMethodWithBodyNotPossible(){
//
// };
//Standard Method CAN be declared in AbstractClass
public void StandardMethod(){
System.out.println("Standard Method in AbstractClass (super) runs");
}
}
public class ConcreteClass
extends AbstractClass
implements ExampleInterface{
//Abstract Method HAS TO be IMPLEMENTED in child class. Implemented by ConcreteClass
@Override
public void AbstractMethod() {
System.out.println("AbstractMethod overridden runs");
}
//Standard Method CAN be OVERRIDDEN.
@Override
public void StandardMethod() {
super.StandardMethod();
System.out.println("StandardMethod overridden in ConcreteClass runs");
}
public void ConcreteMethod(){
System.out.println("Concrete method runs");
}
//A method in interface HAS TO be IMPLEMENTED in implementer class.
@Override
public void MethodInInterface() {
System.out.println("MethodInInterface Implemented by ConcreteClass runs");
// Cannot declare abstract method in a concrete class
// public abstract void AbstractMethodDeclarationInConcreteClassNotPossible(){
//
// }
}
}
Per la mia comprensione:
Metodi astratti:
Solo la classe astratta può contenere metodi astratti. Inoltre, la classe derivata deve implementare il metodo e non viene fornita alcuna implementazione nella classe.
Metodi virtuali:
Una classe può dichiararli e fornire anche l'implementazione dello stesso. Anche la classe derivata deve implementare il metodo per sovrascriverlo.