2011-12-19 2 views
27
object a = new Dog(); 

vsdifferenza tra oggetto un nuovo = Dog() vs Dog a = new Cane()

Dog a = new Dog(); 

In entrambi i casi a.GetType()Dog. Entrambi invocano lo stesso costruttore (con la stessa gerarchia).

Quindi puoi dirmi la differenza tra queste due affermazioni?

+0

La bellezza di ciò che ho trovato è che a volte, un metodo potrebbe aver bisogno di lavorare con più di un 'Cane'. Con l'istruzione 'Object a = new Dog()', puoi usarla in un metodo. Analogamente, puoi fare 'Object b = new Cat()', e puoi usarlo allo stesso modo. –

+1

@ Ken Perché non usare un'interfaccia allora? Come HousePet a = new Dog() ;. Mettere metodi condivisi nell'interfaccia HousePet o un'interfaccia Animal. – Andy

+9

Questo deve essere un duplicato ... – slugster

risposta

36

Entrambi creano un oggetto Dog. Solo il secondo consente di invocare direttamente i metodi Dog o di trattarlo diversamente come un cane, ad esempio se è necessario passare l'oggetto a un metodo come parametro di tipo Dog (o qualcosa nella gerarchia Dog più specifica rispetto al semplice object).

object obj = new Dog(); 
// can only see members declared on object 
var type = obj.GetType(); // can do this 
Console.WriteLine(obj.ToString()); // also this 
obj.Bark(); // Error! Bark is not a member of System.Object 

Dog dog = new Dog(); 
// can do all of the methods declared for Object 
dog.Bark(); // can finally use the method defined for Dog 
+0

Grazie. Sono d'accordo con te. Ma quando scrivo seguendo il codice oggetto a = new Dog(); Console.WriteLine (a.GetType()); Prendo un out come Dog e non Object. Perché è così? – om471987

+3

@Omkarpanhalkar, sta ottenendo il tipo di istanza, non della variabile. Questo è un meccanismo di runtime che ottiene i metadati associati all'istanza e il runtime sa che l'istanza è un cane. Ciò che hai perso sono i benefici in fase di compilazione. –

+0

La prossima volta che mi è stato chiesto qualcosa di simile, userò "Tratta come un xxx", che è la parola più appropriata, per me come madrelingua inglese. –

5

La prima riga crea una variabile di tipo object.

Il compilatore non consente di trattarlo come Dog.

+0

a meno che non lo lanci, ad es. '(Cane) a' –

+1

@PeterOlson Nel qual caso non è più un semplice oggetto' ... – poke

6

new Dog() è un'espressione che crea una nuova istanza Dog. Invoca il costruttore senza parametri della classe Dog.

a è una variabile: una cella di memoria in memoria che contiene un riferimento all'istanza Dog dopo l'assegnazione.

La differenza è che una variabile Dog può contenere solo un riferimento a un'istanza Dog (o un'istanza di qualsiasi classe che deriva da Dog), mentre una variabile object può contenere un riferimento a un object un'istanza (o un'istanza di qualsiasi classe che deriva da object - che fa la classe Dog).

Se si dispone di una variabile Dog, è possibile richiamare qualsiasi metodo definito dalla classe Dog (e le sue classi di base) nell'istanza di riferimento. Quando si dispone di una variabile object, è possibile solo richiamare i metodi della classe object nell'istanza.

+0

Grazie. Sono d'accordo con te. Ma quando scrivo seguendo il codice oggetto a = new Dog(); Console.WriteLine (a.GetType()); Ottengo un fuori come Cane e non Oggetto. Perché è così? – om471987

+1

Perché si richiama il metodo GetType sull'istanza 'Dog' a cui fa riferimento' a'. GetType restituisce il tipo dell'istanza, non della variabile. – dtb

5

Entrambe le istruzioni contengono una dichiarazione e un'invocazione del costruttore. Le invocazioni del costruttore sono identiche, quindi si ottiene un Dog in entrambi i casi. Le dichiarazioni sono diverse: nel primo caso, si dichiara una variabile di tipo object, una superclasse di Dog; nel secondo caso, si dichiara una variabile di tipo Dog. La differenza è che nel codice successivo è possibile richiamare i metodi di Dog senza un cast solo quando si dichiara la variabile come Dog; se lo dichiari come object, avresti bisogno di un cast.

4

Entrambe le istruzioni coinvolgono chiamando il costruttore predefinito di Dog come si dice; pertanto, è evidente che in entrambi i casi viene creata un'istanza Dog. Ciò significa che entrambe le istruzioni terminano con inizializzando una variabile con un'istanza identica (questa è la parte dell'istruzione dopo gli uguali).

Tuttavia, le dichiarazioni hanno anche un'altra parte: la dichiarazione di una variabile (questa è la parte dell'istruzione prima degli uguali). In linguaggi staticamente tipizzati come C#, ogni variabile - più in generale, qualsiasi espressione - ha un tipo statico:

object a = new Dog(); // static type: object/runtime type: Dog 
Dog b = new Dog(); // static type: Dog/runtime type: Dog 

Il compilatore non vi permetterà di assegnare un valore a una variabile che non può dimostrare è del tipo statico della variabile, ad es non permetterebbe

Cat c = new Dog(); // unless Dog derives from Cat, which we know isn't true 

Poiché tutti reference types implicitamente derivano da System.Object, assegnando un Dog a una variabile di tipo statico object è OK. Si può pensare a "tipo statico" come l'oggetto è "dichiarato come". È sempre possibile determinare il tipo statico di qualcosa semplicemente leggendo il codice sorgente; questo è come fa il compilatore.

Quindi c'è anche il tipo di esecuzione di ciascuna variabile (espressione), che ho menzionato sopra. Questo è lo stesso in entrambi i casi, perché dopo tutto in entrambi i casi abbiamo creato uno Dog. Si può pensare a "tipo di runtime" come l'oggetto in realtà è. Il tipo di runtime di qualcosa non può essere determinato solo leggendo la fonte; lo si determina solo mentre il programma è in esecuzione, da cui il nome. In C#, questo è fatto chiamando GetType.

Dovrebbe essere ovvio che il tipo di runtime è qualcosa di cui non si può fare a meno¹; tutto ciò che è deve "essere" qualcosa dopo tutto. Ma perché preoccuparsi di inventare la nozione di tipo statico?

Si può pensare a static types come un contratto tra voi (il programmatore) e il compilatore. Dichiarando che il tipo statico di b è Dog, si comunica al compilatore che non si intende utilizzare tale variabile per la memorizzazione di un valore diverso da Dog. Il compilatore, in cambio, promette di non farti violare il tuo scopo dichiarato e produce un errore se tenti di farlo. Impedisce inoltre di utilizzare d in qualsiasi modo non supportato da ogni tipo di Dog.

considerare:

class Dog { 
    public void Woof(); 
} 

Dog d = new Dog(); 
d.Woof(); // OK 

object o = new Dog(); 
o.Woof(); // COMPILER ERROR 

L'ultima riga causa un errore di compilazione perché viola il contratto di tipizzazione statica: hai detto al compilatore che o può essere qualsiasi cosa derivante da System.Object, ma non tutte le cose derivanti da tale avere un metodo Woof. Quindi il compilatore sta cercando di proteggerti dicendo "Che cosa ci fai lì? Non posso provare che tutto ciò che si trova in o può essere un woof! E se fosse un Cat?".

Note:

¹ Questo non significa che ogni oggetto sa magicamente ciò che "è" in tutte le lingue. In alcuni casi (ad es.in C++) questa informazione potrebbe essere usata quando si crea un oggetto, ma viene "dimenticata" per consentire al compilatore più libertà di ottimizzare il codice. Se ciò accade, l'oggetto è ancora è qualcosa, ma non puoi prenderlo e chiedergli "cosa stai?".

² In realtà, in questo banale esempio può provarlo. Ma non sceglierà di usare questa conoscenza perché onorare il contratto del tipo statico è l'intero punto.

+0

Bella risposta! Questa è una buona spiegazione. Ho solo un paio di piccoli cavilli. Innanzitutto, si dice: "Dovrebbe essere ovvio che il tipo di runtime è qualcosa di cui non si può fare a meno" - e tuttavia ci sono linguaggi tipizzati staticamente senza alcun supporto per il tracciamento dei tipi di runtime. (Ad esempio, le informazioni sul tipo di runtime, RTTI, sono facoltative in C++.) Perché funzioni, il compilatore deve fidarsi ciecamente dell'uso del cast da parte del programmatore: se dici che questo oggetto è in realtà un cane, allora è meglio che sia un Cane in fase di esecuzione, o Cose molto cattive (comportamento indefinito) accadrà. Ma funziona. –

+0

In secondo luogo, potrebbe essere d'aiuto se spiegando che 'GetType()' restituisce il tipo di runtime, hai fornito una breve spiegazione sulla distribuzione del metodo virtuale. (In cima alla mia testa, non sono sicuro che 'GetType()' sia effettivamente una chiamata virtuale, ma è un utile punto di partenza per spiegare il concetto.) L'intera ragione per cui il polimorfismo del sottotipo funziona è a causa del virtuale metodi - cioè, poiché le chiamate al metodo possono essere inviate in base al tipo di runtime, non al tipo statico. In effetti, puoi avere il polimorfismo senza tipi statici: basta guardare Python o Ruby. –

+0

@DanielPryden: Riguardo al tipo di runtime, ho riformulato il passaggio in modo che sia chiaro che l'oggetto è ancora * è * qualcosa (ad esempio potrebbe avere un vtable che decide esattamente cosa), ma non puoi chiedere di ottenere questa informazione indietro.Riguardo al polimorfismo il tuo punto è completamente corretto; quello era semplicemente un brutto pensiero dell'ultimo minuto da parte mia, che volevo rimuovere per le ultime ore che sono stato AFK. – Jon

2

Questo è utile quando si desidera utilizzare il polimorfismo ed è possibile utilizzare il metodo astratto con implementazione in Dog. Pertanto, in questo modo l'oggetto è Dog, anche così Object. Quindi puoi usare questo modo quando vuoi usare il polimorfismo.