views:

350

answers:

10

Can someone tell me why the line with "//Compiles" compiles, and why the line with "//Doesn't Compile" does not?

I don't understand why A would be implicitly convertible to B, not the other way round.

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");
 }
}

thanks!

A: 

A is not implicitly convertible to B. B is convertible to A.

In

foo = bar

it's going to try to convert 'bar' to the type of 'foo'.

(That is, I think you are just misinterpreting how 'assignment' works with regards to implicit conversions.)

Brian
+5  A: 

Let's change the name of the classes from AClass to Mammal and BClass to 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.

Maybe not the best example, but it may be enough for you to understand.

Alfred Myers
Can't talk about OOP without a trip to the zoo. =)
JohnFx
just don't mention the Platypus !
Mitch Wheat
Nitpick: "Mammals".
McPherrinM
A: 

This has little to do with C#; it's basic inheritance. a is not of type BClass. What if BClass had additional fields/properties? What happens when you attempt to access one of those members on a?

TrueWill
A: 

Because all instances of BClass are also AClass since BClass inherits from AClass. AClass is less specific than BClass hence you can implicitly convert from B to A

Igor Zevaka
A: 

BClass is a subclass of AClass (or AClass is a superclass of BClass), and the subclass relationship is an "is a" relationship. So if b is an instance of BClass, it is also an instance of AClass. This is why it is fine to point to b with the variable a, but it is not OK to point to a with b, because that requires an additional assumption.

elcelista
+8  A: 

Because B does everything that A does but A does not necessarily do everything that B does. Think of it this way:

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.
tster
+1 nice explaination
Chuck Conway
+3  A: 

An object instantiated from a class may be treated as the type of any of its super-classes, but cannot be treated as the type of a subclass.

  • A subclass can be treated as its superclass, but never in the opposite direction.


In more abstract terms:

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");
    }
}
Chris Ballance
I guess what I don't understand is that when I debug this, once a = b gets executed, a is shown as a "HarelyDavidson" type in the debugger. I don't know how this can be...
Easy. The debugger calls .GetType().Motorcycle's GetType() returns Motorcycle.HarleyDavidson's GetType() returns HarleyDavidson.You get the effective type, not the declared type.
Mike Christiansen
@HC see my update, I hope that helps clarify things.
Chris Ballance
@Mike Christiansen, nice clarification
Chris Ballance
@HC: a = b does not "convert" the HarleyDavidson into a Motorcycle. "a" and "b" will both be references to the same object, but "b" will will be able to acces the HarleyDavidson members and "a" will only be able to access the Motorcycle members. The actual object will still be a HarleyDavidson.
Lucas
A: 

Possibly you were confused about which was which, as your question asks "Why is implicit conversion allowed from superclass to subclass?".

Actually, it's the other way around. A subclass is an instance of the superclass, but not the other way around, so that's why the types are not compatible.

Imagine a very small superclass with just a single method or constant. Now imagine a subclass that defines everything including a kitchen sink. These are almost completely different types. However, the subclass is still an instance of the superclass, it does have that one method or constant.

The superclass, on the other hand, has almost nothing that the inherited class does. However, "almost" is good enough for the parent to still be an instance of the child class as well. But passing the parent class to a method expecting the child would obviously not work, as hardly anything is available.

DigitalRoss
A: 

Sort of paraphrasing what everyone else had said. I don't know if this makes it any clearer for you.

'a' is declared as an object of type AClass which supports the AMethod() method.

'b' is declared as an object of type BClass which supports the BMethod() method and, being a subclass of AClass, will also support the AMethod() method as it inherits it from its parent superclass.

So you can easily assign an object of type BClass to a variable of type AClass as the compiler will expect to only ever call AMethod() on it which is fine.

However, you can't assign an object of type AClass to a variable of type BClass as the compiler might expect to have to call either AMethod() or BMethod() on it and, of course, it won't be able to do the latter as an AClass object just won't support it.

Trevor Tippins
+3  A: 

This directly follows from the Liskov Substitution Principle:

Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T

In other words, the derived class can always be used in place of the base class. It's usually not possibel the other way round - because the base class can't do what the derived class does.

(I know I am mixing up the timeline here - inheritance was first, Liskov came second - but she put nicely how inheritance is intended to be used)

peterchen
+1 for blinding me with science
Chris Ballance
hehe :) at your service!
peterchen