views:

425

answers:

5

Hello,

I have a problem (it's my fault, I just can't spot what I'm doing wrong) where "ToString" isn't calling the correct method...

public class ClassA
{
   public override ToString()
   {
      return "Hello, I'm class A.";
   }
}

public class ClassB : ClassA
{
   public override ToString()
   {
       return "Hello, I'm class B.";
   }
}

ClassB myClassB = new ClassB();
List<ClassA> myClassAList = new List<ClassA>();

myClassAList.Add((ClassA) myClassB);
ClassA tempClassA = myClassAList[0];
Console.WriteLine(tempClassA.ToString());

I'm getting the "ToString" from "ClassB" and not "ClassA" what am I doing wrong?

A: 

ToString is a virtual method, it doesn't matter what the type of your variable is, it matters what the type of the actual object is.

If the method wasn't virtual, a call would go to the method the compiler would know about, which would be the ClassA ToString method.

Virtual methods are implemented through a lookup table, bound to the object type. Since the object you end up with in variable "tempClassA" is really an object of type ClassB, the lookup table for ClassB is used, and thus the ToString method for that class is used.

Lasse V. Karlsen
+6  A: 

You are not doing anything wrong -- this is how polymorphism virtual methods work. When you put ClassB into collection of ClassA references, it is still ClassB object. Invoking .ToString() will always find ClassB.ToString() if the object is indeed of ClassB.

Srdjan Jovcic
+4  A: 

You are overriding ToString in ClassB instead of hiding it from the original which will cause the overridden method to take precedence. What you could do is..

public class ClassA
{
    public override string ToString()
    {
     return "Hello, I'm class A.";
    }
}

public class ClassB : ClassA
{
    public new string ToString()
    {
     return "Hello, I'm class B.";
    }
}

...

List<ClassA> theList = new List<ClassA>
{
    (ClassA)new ClassB(),
    (ClassA)new ClassB()
};

ClassA a = theList[0];
Console.WriteLine(a.ToString());

// OR... 

Console.WriteLine(new ClassA().ToString());  // I'm Class A
Console.WriteLine(new ClassB().ToString());  // I'm Class B
Console.WriteLine(((ClassA)new ClassB()).ToString()); // I'm Class A
Quintin Robinson
This doesn't change a thing. You cannot break `ClassA`'s interface contract here.
Konrad Rudolph
If you were to hide the ToString method via new and then downcast ClassB to ClassA and call ToString() you would get ClassA's overriden ToString method. Try it out Console.WriteLine(((ClassA)new ClassB()).ToString());
Quintin Robinson
I tried; you are wrong.
Konrad Rudolph
+1 I also tried, and Quintin is correct
Patrick McDonald
I will post the code I used to test, it seems to be working for me and I would sincerely like to know what I am doing incorrectly..
Quintin Robinson
I'll go eat dung. I forgot `override` in `ClassA` and mono interpreted this (correctly?) to not override `ToString` at all so that my output was merely `ClassB` (i.e. `GetType().ToString()`).
Konrad Rudolph
lol I wouldn't worry about it, I really thought I was doing something wrong when you said it didn't work but afaik, if you don't specify either new or overrides, the resulting output is the same as if you specified new but the IDE gives you a compiler warning.
Quintin Robinson
A: 

You are getting the correct results. You add an instance of ClassB to your list; even though you treat it as a ClassA. Therefore, when you call the virtual ToString method, it will result in a call to ClassB's ToString; because that is the actual kind of object you are working with.

driis
A: 

Thanks for all your super fast answers that clears things up.

So... to display the ToString of "ClassA" from "ClassB" to a user would you recommend that I create another "Display" object, something like "ClassADisplay" and copy the variables from "myClassB" to my new "ClassADisplay" ?

Thanks again all.

paulio