1

I tried casting a new variable/object to it's base class but when I debug I notice it has the class members of the derived class. So when I call an over ridden method it calls the derived class instead of the base even though the object is declared as the base.

Person person = new Student("Bill", 3.5, "chem");
Console.WriteLine(person.ToString());
....
public class Person
{
       string  name;
       public override string ToString() => this.name + " age:" + this.age;
}

public class Student : Person
{
    string major;
    double GPA;
    public override string ToString() => Name + " major:" + major + " GPA: " + GPA;
}

To remedy this I need to define the ToString() method in the derived class as new. But I don't understand why as I'm casting to the base so it should use the base method? Is it due to the way the compiler treats reference objects? The new object of base class still references the derived class memory instead of copying the appropriate data to it's own memory block.

mr fat
  • 75
  • 1
  • 9

2 Answers2

3

Yes and that's known as Polymorphism or run time polymorphism. It's happening so cause the actual type is Student even though the declared type is person in your below code line. So the method call happens on the actual type and not on the declared type.

Person person = new Student("Bill", 3.5, "chem");
Rahul
  • 76,197
  • 13
  • 71
  • 125
3

There are three keywords needed to control this behaviour with polymophism. Virtual, Override, and New. These are explained well here, and from MSDN:

Override:

The override modifier may be used on virtual methods and must be used on abstract methods. This indicates for the compiler to use the last defined implementation of a method. Even if the method is called on a reference to the base class it will use the implementation overriding it.

New:

The new modifier instructs the compiler to use your child class implementation instead of the parent class implementation. Any code that is not referencing your class but the parent class will use the parent class implementation.

An Example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BindingExample
{
    public class SomeBaseClass
    {
        public virtual string CustomString() => "From SomeBaseClass";
    }

    public class FirstSpecialisation : SomeBaseClass
    {
        public override string CustomString() => "From FirstSpecialisation";
    }

    public class SecondSpecialisation : SomeBaseClass
    {
        public new string CustomString() => "From SecondSpecialisation";
    }

    class Program
    {
        static void Main(string[] args)
        {
            // Base Example, as expected
            SomeBaseClass a1 = new SomeBaseClass();

            Console.WriteLine(a1.CustomString());

            // First Example, both output the same result
            FirstSpecialisation b1 = new FirstSpecialisation();
            SomeBaseClass b2 = b1;

            Console.WriteLine(b1.CustomString());
            Console.WriteLine(b2.CustomString());

            // Second Example, output different results
            SecondSpecialisation c1 = new SecondSpecialisation();
            SomeBaseClass c2 = c1;

            Console.WriteLine(c1.CustomString());
            Console.WriteLine(c2.CustomString());

            Console.ReadLine();
        }
    }
}

Output:

From SomeBaseClass
From FirstSpecialisation
From FirstSpecialisation
From SecondSpecialisation
From SomeBaseClass

Note the above behaviour with the SecondSpecialisation class on lines 4 and 5.

This is particularly interesting in your example, because you are overriding a method with 2 depths. If you use overide twice, an unusual behaviour is evoked; the last override expressed takes precedence. Hence, you must use the new keyword.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BindingExample
{
    public class SomeBaseClassA
    {
        public override string ToString() => "From SomeBaseClassA";
    }

    public class SpecialisationA : SomeBaseClassA
    {
        public override string ToString() => "From SpecialisationA";
    }

    public class SomeBaseClassB
    {
        public new string ToString() => "From SomeBaseClassB";
    }

    public class SpecialisationB : SomeBaseClassB
    {
        public new string ToString() => "From SpecialisationB";
    }

    class Program
    {
        static void Main(string[] args)
        {
            // First Example
            SomeBaseClassA a1 = new SomeBaseClassA();
            Console.WriteLine(a1);

            SpecialisationA a2 = new SpecialisationA();
            Console.WriteLine(a2);

            SomeBaseClassA a3 = a2;
            Console.WriteLine(a3);

            // Second Example
            SomeBaseClassB b1 = new SomeBaseClassB();
            Console.WriteLine(b1.ToString());

            SpecialisationB b2 = new SpecialisationB();
            Console.WriteLine(b2.ToString());

            SomeBaseClassB b3 = b2;
            Console.WriteLine(b3.ToString());

            Console.ReadLine();
        }
    }
}

Output:

From SomeBaseClassA
From SpecialisationA
From SpecialisationA
From SomeBaseClassB
From SpecialisationB
From SomeBaseClassB

I did observe when overriding the ToString method that it was called on Object. I would assume this is because the Console.WriteLine method can take an object, and because the new keyword was used on the 'ToString' methods within the specialisations, the compiler calls Object.ToString instead. This is something you may wish to be mindful of.

Community
  • 1
  • 1
Élie
  • 1,285
  • 1
  • 12
  • 27