2009-09-24 11 views
5

Qualcuno può dirmi perché compila la riga con "// Compiles" e perché la riga con "// Does not Compile" non lo è?Perché la conversione implicita è consentita dalla superclasse alla sottoclasse?

Non capisco perché A sia implicitamente convertibile in B, non viceversa.

public class SomeClass { 

static public void Test() { 
    AClass a = new AClass(); 
    BClass b = new BClass(); 

    a = b; // Compiles 
    b = a; // Doesn't compile 
} 
} 

public class AClass { 
public void AMethod() { 
    Console.WriteLine("AMethod"); 
} 
} 

public class BClass : AClass { 
public void BMethod() { 
    Console.WriteLine("BMethod"); 
} 
} 

grazie!

risposta

1

A non è implicitamente convertibile a B. B è convertibile in A.

In

foo = bar 

sta andando a cercare di convertire 'bar' del tipo di 'foo'.

(Cioè, penso che tu sia semplicemente interpretando male come 'assegnazione' funziona per quanto riguarda le conversioni implicite.)

8

Cambiamo il nome delle classi da AClass a Mammifero e BClass a Dog.


a = b; // you're putting a dog on a variable of type Mammal. That's OK. 
b = a; // You're putting a mammal (could be a cat, a monkey, etc.) on a variable of type Dog. 

Forse non il miglior esempio, ma può essere sufficiente per voi capire.

+7

non posso parlare di OOP senza una visita allo zoo . =) – JohnFx

+0

non menzionare l'ornitorinco! –

+0

Nitpick: "Mammiferi". – McPherrinM

1

Questo ha poco a che fare con C#; è ereditarietà di base. a non è di tipo BClass. Cosa accadrebbe se BClass avesse campi/proprietà aggiuntivi? Cosa succede quando tenti di accedere a uno di questi membri su un?

1

Perché tutte le istanze di BClass sono anche AClass dal BClass eredita da AClass. AClass è meno specifico rispetto BClass Quindi si può convertire implicitamente da B ad A

1

BClass è una sottoclasse di AClass (o AClass è una superclasse di BClass), e il rapporto sottoclasse è un "è una" relazione. Quindi, se 2012 è un'istanza di BClass, è anche un'istanza di AClass. Questo è il motivo per cui va bene puntare a b con la variabile a, ma non è corretto puntare a a con b, perché ciò richiede un'ulteriore ipotesi.

15

Poiché B fa tutto ciò che A fa ma A non fa necessariamente tutto ciò che fa B. Pensate in questo modo:

AClass --> Shape 
BClass --> Circle 

Shape a = new Shape(); 
Circle b = new Circle(); 

a = b; // works because a is of type "Shape" and a circle is a specific shape 
b = a; // doesn't work because b is of type "Circle" and a could be a square. 
+1

+1 bella spiegazione –

4

Un oggetto istanziato da una classe possono essere trattati come il tipo di uno dei suoi super-classi, ma non possono essere trattati come il tipo di una sottoclasse.

  • Una sottoclasse può essere considerata come la sua superclasse, ma mai nella direzione opposta.

In termini più astratti:

public class HarleyExample 
{ 
    static public void Test() 
    { 
     Motorcycle a = new Motorcycle(); 
      HarleyDavidson b = new HarleyDavidson(); 
      Motorcycle c = new Motorcycle(); //Just a plain motorcycle 
      a = b; // A Harley can be treated as a regular motorcycle 
      //b = a; // Not just any motorcycle is a Harley 

      Console.WriteLine("Is A a motorcycle? " + (a is Motorcycle)); 
      Console.WriteLine("Is A a harley?  " + (a is HarleyDavidson)); 
      Console.WriteLine(); 
      Console.WriteLine("Is B a motorcycle? " + (b is Motorcycle)); 
      Console.WriteLine("Is B a harley?  " + (b is HarleyDavidson)); 
      Console.WriteLine(); 
      Console.WriteLine("Is C a motorcycle? " + (c is Motorcycle)); 
      Console.WriteLine("Is C a harley?  " + (c is HarleyDavidson)); 

      Console.ReadKey(); 
    } 
} 

public class Motorcycle 
{ 
    public void Cruise() 
    { 
     Console.WriteLine("Cruising"); 
    } 
} 

public class HarleyDavidson : Motorcycle 
{ 
    public void CruiseInStyle() 
    { 
     Console.WriteLine("Cruising in style on a Harley"); 
    } 
} 
+0

Immagino che quello che non capisco è che quando eseguo il debug di questo, una volta che a = b viene eseguito, a viene mostrato come un tipo "HarelyDavidson" nel debugger. Non so come possa essere ... –

+1

Facile.Il debugger chiama .GetType(). Motorcycle's GetType() restituisce Motorcycle. HarleyDavidson's GetType() restituisce HarleyDavidson. Si ottiene il tipo effettivo, non il tipo dichiarato. –

+0

@HC vedi il mio aggiornamento, spero che aiuti a chiarire le cose. –

1

Forse si erano confusi su quale fosse che, come la tua domanda si chiede "perché è la conversione implicita consentito dalla superclasse sottoclasse?".

In realtà, è il contrario. Una sottoclasse è un'istanza della superclasse, ma non il contrario, ecco perché i tipi non sono compatibili.

Immagina una superclasse molto piccola con un solo metodo o costante. Ora immagina una sottoclasse che definisca tutto compreso un lavello della cucina. Questi sono tipi quasi completamente diversi. Tuttavia, la sottoclasse è ancora un'istanza della superclasse; ha quel metodo o costante.

La superclasse, d'altra parte, non ha quasi nulla che la classe ereditata implementa., "Quasi" è abbastanza buono perché il genitore possa essere ancora un'istanza della classe figlio, ma passare la classe genitore ad un metodo che si aspetta molto probabilmente il bambino non avrebbe funzionato, poiché quasi nulla è disponibile.

1

Una sorta di parafrasi di ciò che tutti avevano detto. Non so se questo ti rende più chiaro per te.

'un' viene dichiarata come un oggetto di tipo AClass che supporta il metodo AMethod().

'b' è dichiarata come un oggetto di tipo BClass che supporta il metodo BMethod() e, essendo una sottoclasse di AClass, sosterrà anche il metodo AMethod() come eredita dalla sua superclasse genitore.

Così si può facilmente assegnare un oggetto di tipo BClass ad una variabile di tipo AClass come il compilatore si aspetta di chiamare sempre e solo AMethod() su di esso che va bene.

Tuttavia, non è possibile assegnare un oggetto di tipo AClass a una variabile di tipo BClass poiché il compilatore potrebbe aspettarsi di dover richiamare AMethod() o BMethod() e, ovviamente, non lo farà essere in grado di fare quest'ultimo come un oggetto AClass non lo supporterà.

4

segue questo direttamente dal Liskov Substitution Principle:

Sia q (x) una struttura dimostrabile sugli oggetti x di tipo T. Quindi q (y) dovrebbe essere vero per oggetti y di tipo S dove S è un sottotipo di T

In altre parole, la classe derivata può sempre essere utilizzata al posto della classe base. Di solito non è possibile il contrario - perché la classe base non può fare ciò che fa la classe derivata.

(so che sto mescolando la timeline qui - eredità era prima, Liskov arrivato secondo - ma ha messo ben come l'ereditarietà è destinato ad essere utilizzato)

+2

+1 per accecarmi con la scienza –

+0

hehe :) al vostro servizio! – peterchen

Problemi correlati