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.
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. –
@ Ken Perché non usare un'interfaccia allora? Come HousePet a = new Dog() ;. Mettere metodi condivisi nell'interfaccia HousePet o un'interfaccia Animal. – Andy
Questo deve essere un duplicato ... – slugster