2010-05-12 16 views
13

Stavo passando per C# Brainteasers (http://www.yoda.arachsys.com/csharp/teasers.html) e ho trovato una domanda: quale dovrebbe essere l'output di questo codice?Comportamento diverso del metodo di overloading in C#

class Base 
{ 
    public virtual void Foo(int x) 
    { 
     Console.WriteLine ("Base.Foo(int)"); 
    } 
} 

class Derived : Base 
{ 
    public override void Foo(int x) 
    { 
     Console.WriteLine ("Derived.Foo(int)"); 
    } 

    public void Foo(object o) 
    { 
     Console.WriteLine ("Derived.Foo(object)"); 
    } 
} 

class Test 
{ 
    static void Main() 
    { 
     Derived d = new Derived(); 
     int i = 10; 
     d.Foo(i); // it prints ("Derived.Foo(object)" 
    } 
} 

Ma se cambio il codice per

class Derived 
{ 
    public void Foo(int x) 
    { 
     Console.WriteLine("Derived.Foo(int)"); 
    } 

    public void Foo(object o) 
    { 
     Console.WriteLine("Derived.Foo(object)"); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Derived d = new Derived(); 
     int i = 10; 
     d.Foo(i); // prints Derived.Foo(int)"); 

     Console.ReadKey(); 
    } 
} 

voglio perché l'uscita è sempre cambiato quando stiamo ereditando vs non ereditare; perché l'overloading dei metodi si comporta diversamente in questi due casi?

risposta

18

Come ho specificato nella pagina answers:

Derived.Foo (oggetto) viene stampato - nella scelta di un sovraccarico, se ci sono dei metodi compatibili dichiarati in una classe derivata, tutte le firme dichiarati nel la classe base viene ignorata - anche se sono sovrascritte nella stessa classe derivata!

In altre parole, il compilatore esamina i metodi che sono appena dichiarati nella classe più derivata (in base al tipo di espressione in fase di compilazione) e vede se sono applicabili. Se lo sono, utilizza il "migliore" disponibile. Se nessuno è applicabile, prova la classe base e così via. Un metodo sottoposto a override non viene considerato come dichiarato nella classe derivata.

Vedere le sezioni 7.4.3 e 7.5.5.1 della specifica C# 3 per ulteriori dettagli.

Ora come per esattamente perché è specificato in questo modo, non lo so. Per me è logico che i metodi dichiarati nella classe derivata abbiano la precedenza su quelli dichiarati nella classe base, poiché altrimenti si verifica il problema della "base di base fragile" - l'aggiunta di un metodo nella classe base potrebbe cambiare il significato del codice usando il classe derivata. Tuttavia, se la classe derivata è ignorando il metodo dichiarato nella classe base, è chiaramente a conoscenza di esso, in modo che l'elemento di fragilità non si applichi.

+0

perdona la mia ignoranza, ma perché considera il metodo con parametro di tipo "oggetto" come il migliore, penso che int sia più vicino (come quello che sta accadendo in 2 ° caso) – Wondering

+0

@Wondering: Sì, 'int' è meglio - ma il sovraccarico di 'int' sta già sovrascrivendo il metodo nella classe base, quindi non viene preso in considerazione: non conta come" appena dichiarato "nella classe derivata. Questo è il punto centrale di tutta questa spiegazione :) –

+0

Grazie Jon per tutto il tempo e l'aiuto. Ho capito il punto. – Wondering

0

È perché le firme dei metodi nella classe derivata vengono confrontate prima prima di considerare le firme della classe base. Pertanto, quando si tenta:

d.Foo(i); 

tenta di abbinare la firma del metodo contro la vostra classe corrente (non la classe base). Trova una corrispondenza accettabile Foo(object) e non considera ulteriormente le firme del metodo della classe base.

Si tratta del compilatore che trova una firma del metodo corrispondente e l'ordine di ricerca che utilizza per trovare queste firme.

-Doug

+0

La parte sorprendente è che * non * include il metodo di override nella classe derivata. Questa è ancora una "firma del metodo nella classe derivata" ma non viene considerata come dichiarata nella classe derivata. –

+0

"Trova una corrispondenza accettabile Foo (oggetto)" ma, Foo (int) è anche una corrispondenza accettabile – Wondering

+0

Sì, ma come nota Jon, Foo (int) non viene dichiarato nella classe derivata (anche se è sovrascritto, cosa che non contare). – Doug

1

Riguarda l'ambito. Nel primo programma, void Foo (int i) appartiene alla classe Base. la classe Derived si limita a ridefinire il suo comportamento.

Foo (int i) viene ignorato perché viene "preso in prestito" e ridefinito per via ereditaria dalla classe Base. se Foo (oggetto o) non esistesse, allora sarebbe usato Foo (int i).

Nel secondo programma, void Foo (int i) viene chiamato perché appartiene propriamente alla classe Derived (vale a dire non viene preso in prestito e annullato tramite l'ereditarietà) e ha la migliore adatta alla firma.

+0

buona spiegazione. – Wondering

0

La vera domanda è perché i progettisti di C# credono che la risoluzione di sovraccarico debba ignorare i metodi sovrascritti in questa situazione?

Sono problemi come questo che rendono il metodo di sovraccarico una delle mie funzionalità linguistiche preferite.

+0

Il sovraccarico tra le classi in una gerarchia di ereditarietà tra più firme applicabili è sempre molto complicato. Sono d'accordo sul fatto che ignorare il metodo sovrascritto mi sembra un errore, ma penso che la situazione non sia comunque chiara. Preferisco evitare l'ereditarietà, o evitare di avere sovraccarichi multipli che possono essere applicabili per lo stesso argomento. –

Problemi correlati