Почему мы назначаем объект дочернего класса исходной переменной родительского класса?

У меня есть следующий код.

public class Parent
{
    public void Print()
    {
        Console.WriteLine ("Parent Method");
    }
}

public class Child : Parent
{
    public new void Print()
    {
        Console.WriteLine ("Child Method");
    }
}

public class Program
{
    public static void Main()
    {
        Child C = new Child();
        C.Print();
    }
}

Если я запустил этот код, я получаю результат "Child Method" Но если я сделаю следующее, почему я получаю результат "Родительский метод"?

public class Parent
{
    public void Print()
    {
        Console.WriteLine ("Parent Method");
    }
}

public class Child : Parent
{
    public new void Print()
    {
        Console.WriteLine ("Child Method");
    }
}

public class Program
{
    public static void Main()
    {
        Parent P = new Child();   // The only difference is this.
        P.Print();
    }
}

Единственное отличие:

Child C = new Child();   
Parent P = new Child();

Я думал, что new Child() означает, что мы создаем экземпляр класса Child. И я думал, что оба, C и P, являются просто объектными ссылочными переменными, которые содержат расположение экземпляра класса Child.
Можете ли вы исправить меня, если я ошибаюсь или скажу, если я пропущу что-то, потому что я не понимаю, почему в вышеуказанных случаях я получаю разные результаты?

Ответ 1

Это потому, что вы обновили метод Print в Child. Поэтому во время компиляции P.Print() разрешается Parent.Print, но C.Print() разрешается Child.Print(). Если у вас был виртуальный метод, который был переопределен в Child вместо этого, они оба напечатали "Child Method":

public class Parent
{
    // Declare this as virtual, allowing it to be overridden in
    // derived classes. The implementation will depend on the
    // execution-time type of the object it called on.
    public virtual void Print()
    {
        Console.WriteLine ("Parent Method");
    }
}

public class Child : Parent
{
    // Override Parent.Print, so if Print is called on a reference
    // with compile-time type of Parent, but at execution it
    // refers to a Child, this implementation will be executed.
    public override void Print()
    {
        Console.WriteLine ("Child Method");
    }
}

по теме:

Ответ 2

Вот что происходит под капотом

Child C = new Child();
C.Print();

Потому что вы скрываете методы, а не переопределяете их. поэтому метод Print в Parent скрыт и вызывается функция Child.Print().

введите описание изображения здесь

 Parent P = new Child();   // The only difference is this.
 P.Print();

Это (New Child()) будет вызываться в справочнике типа родителя.

введите описание изображения здесь

если вы попробуете это, ссылка типа родителя будет выбрана для типа Child. Отныне вызывается Child.Print().

Parent P = new Child(); 
((Child)P).Print();

введите описание изображения здесь

вывод будет: Детский метод

Ответ 3

Поскольку метод Print не является virtual, компилятор не будет генерировать код, чтобы сообщить CLR, чтобы вызвать метод в соответствии с фактическим типом времени выполнения.

Итак, когда ваша переменная называется Child, будет вызвана Child.Print. Но, когда вы обращаетесь к нему как Parent, Parent.Print будет использоваться.

Использование ключевого слова new предназначено только для теневого копирования, чтобы сообщить компилятору, что дочерний метод действительно скрывает родительский метод и не отменяет его, но не имеет никакого эффекта.

Ответ 4

JCronin, вы отправляете родителя с родителем P = new Child(). Чтобы получить поведение класса Child, вам нужно либо вернуть его обратно ребенку (var x = (Child) P), либо создать экземпляр экземпляра Child (Child P = new Child()).

См. Downcasting

Ответ 5

Потому что вы объявили тип P Parent. Когда вы назначаете ему Child, он будет передан этому типу.