1

Из определения: Апкаст - приведение экземпляра производного класса к базовому

Фрагмент кода апкаста для примера. Класс Human производный от класса Mammal.

Human human = new Human();
Mammal mammal = (Mammal)human; //Upcast
  1. Если апкаст - это приведение экземпляра производного класса к базовому, то в данном случае экземпляр human класса Human должен был быть приведен к классу Mammal. Но после этого фрагмента кода у экземпляра human сохраняются методы и переменные производного класса. В чем тогда выражается приведение к базовому классу?
  2. Если апкаст - это создание нового экземпляра базового класса на основе экземпляра производного (в данном случае mammal из human), то в чем разница между указанным выше кодом с апкастом и данным кодом: Human human = new Human; Mammal mammal = new Mammal(); - ведь в итоге всё равно создается два экземпляра класса - производный и на основе его базовый?

P.S.: Я думаю, что мой вопрос не дублирует этот. Мой вопрос о технической стороне - почему апкаст был, а функционал остался как у производного класса.

faritowich
  • 105
  • 1
  • 1
  • 14
  • 1
    Так ведь human остаётся со своим значением, а вот новый экземпляр базового класса mammal создаётся из human без использования конструктора, только привидением. – Вася Воронцов Feb 05 '21 at 21:23
  • Самое простое - таким образом вы можете например засунуть кучу разных животных в 1 список. То есть в List<Mammal> влезет объект любого типа, наследующего класс Mammal. Пример нужен, или так понятно? – aepot Feb 05 '21 at 21:26
  • Касты никоим образом не меняют тип объекта, поля и методы никуда не могут таинственным методом исчезнуть из памяти, можете убедиться вызвав GetType(). Меняется лишь интерфейс для взаимодействия с реальным объектом, а за допустимостью этого следит компилятор. – SmorcIRL Feb 05 '21 at 22:07
  • 1
    В первом случае у вас создаться только один объект, на который будут указывать 2 ссылки типов Human и Mammal. Во втором случае вы создаете 2 объекта двух разных типов. – SmorcIRL Feb 05 '21 at 22:13
  • 2
    Апкаст не изменяет объект, не создает экземпляр, он просто меняет тип контейнера объекта. Вот у вас есть Mammal mammal = (Mammal)human;, а теперь попробуйте Console.WriteLine(mammal.GetType().Name);, и вы увидите, что реальный тип экземпляра не поменялся. После апкаста возможен даункаст с к тому типу, с которого был апкаст, например Human h = (Human)mammal, но даункаст возможен только если объект соответствует типу, к которому вы его даункастите. То есть объект Mammal m = new Mammal() выдаст ошибку InvalidCastException при попытке даункаста Human h = (Human)m. – aepot Feb 05 '21 at 22:18
  • 1
    Но после этого фрагмента кода у экземпляра human сохраняются методы и переменные производного класса - как ты это проверил? В дубликате явно приведен пример, когда это не так. – Grundy Feb 06 '21 at 00:20

1 Answers1

1
  1. Приведение типов никак не меняет содержимое объектов, т.е. при приведении не создаётся новых экземпляров классов.
  2. Вот здесь весьма подробно расписано в каких случаях необходимо использовать upcast. Но, пожалуй, в 95% случаев осуществлять явный upcast не имеет смысла. Компилятор C# весьма умный в этом отношении и приведение типов осуществляет неявным образом максимально комфортно для разработчиков.

Нижеприведенный пример демонстрирует базовые возможности объекта, приведенного к родительскому классу.

Обратите внимание на строчку mammal.AnyHumanMethod(); Она вызывает ошибку компиляции. C# является строго типизированным языком. Компилятор следит, чтобы обращения ко всем членам класса производилось именно у того типа, к которому приведен объект.

class Program
{
    class Mammal
    {
        public virtual string Name => "It is a mammal";
    }
class Human : Mammal
{
    public override string Name =&gt; &quot;It is a human&quot;;

    public void AnyHumanMethod()
    {
        Console.WriteLine(&quot;Method was called&quot;);
    }
}

static void Main()
{
    var human = new Human();
    Console.WriteLine(human.Name); // Будет выведено &quot;It is a human&quot;
    human.AnyHumanMethod(); // Этот вызов корректен и допустим

    var mammal = (Mammal)human;

    // Сейчас всё равно будет вызван метод Human, т.к. несмотря на
    // приведение, в переменной mammal хранится экземпляр класса Human
    Console.WriteLine(mammal.Name); // Будет выведено &quot;It is a human&quot;

    // У класса Mammal нет метода AnyHumanMethod, он есть только в классе Human
    mammal.AnyHumanMethod(); // Ошибка компляции!

    // Никакого создания нового объекта не происходит при приведение типов.
    // Экземпляры классов human и mammal равны.
    Console.WriteLine($&quot;Сейчас будет возвращено значение True: {human == mammal}&quot;);
}

}