Разница между приведением типов и вызовом полиморфной функции?

#c# #casting #polymorphism

#c# #Кастинг #полиморфизм

Вопрос:

Я читаю книгу Рихтера по c #, и мне интересно, как работает приведение типов.

Допустим, у меня есть этот код:

     class Parent
    {
        public virtual void DoStuff()
        {
            //... some code
        }
    }

    class Child : Parent
    {
        public override void DoStuff()
        {
            // .. some different code
        }
    }
  

Теперь в функции Main() я вызываю это:

         Parent child = new Child();
        child.DoStuff();
  

Чем это отличается от приведения типов? Согласно Рихтеру, среда CLR будет следовать по ссылке дочернего элемента на объект дочернего типа (Child) в памяти и извлекать указатель на дочерний элемент.Версия метода doStuff(). Для меня это звучит как та же операция, что и эта:

         ((Child)child).DoStuff();
  

Где я не прав?

Комментарии:

1. To me that sounds like the same operation as this: Ну, это так (если вы прищуритесь достаточно сильно) — но его проще написать (и совершенно безопасно — вы знаете , что «приведение» будет работать в child.DoStuff() ). Почему вы хотите явно выполнить приведение? Обратите внимание, что это было бы не эквивалентно, если бы new использовалось вместо override . Также имейте в виду, что вызывающий код DoStuff может быть полностью отделен от кода, выполняющего создание экземпляра, — так что первый не знает, является ли (Child) приведение безопасным или нет (т. Е. это ясно из вашего примера кода, но только потому, что две строки смежны).

2. Попробуйте добавить третий класс, который наследует Parent (т.Е. Child2 ), и попробуйте привести к Child2 , когда переменная уже создана с помощью Child

3. Это не то же самое, что приведение. Действие public override void DoStuff() заменяет DoStuff сайт вызова, так что все экземпляры Child будут вызывать это DoStuff независимо от типа переменной, которая содержит ссылку на них. Приведения нет. Существует только один DoStuff для объекта типа Child . Приведение не изменяет тип экземпляра.

4. Хорошо, но как насчет производительности? Имеет дочерний элемент. Стоимость doStuff() такая же, как и ((дочерний)элемент child). doStuff()? В отличие от Parent p = new Parent(); p.doStuff();? Или еще лучше — вызов метода из структуры (который нельзя переопределить)?

5. Ok but how about performance? ericlippert.com/2012/12/17/performance-rant В реальном мире очень немногие системы будут сталкиваться с такого рода проблемами в качестве узкого места в производительности (по сравнению, скажем, с доступом к базе данных).

Ответ №1:

Чем это отличается от приведения типов?

Полиморфизм сильно отличается от приведения типов и является ключевой основой языков программирования OO. Ваш пример на самом деле ничего не демонстрирует о полезности этой функции.

Рассмотрим следующий пример:

  abstract class Animal {
     public abstract string MakeSound(); }

 void PrintSound(Animal animal) { Console.WriteLine(animal?.MakeSound(); }
  

А теперь типичный сценарий callsite с аргументом, известным только во время выполнения:

 var animal = makeUserChooseAnimal(); //no idea what animal this will be
PrintSound(animal);
  

Где именно вы вписались бы в актерский состав, о котором вы спрашиваете? Но полиморфизм и виртуальный вызов сделают возможным, что независимо от того, какому животному на самом деле передается, PrintSound будет вызвано подходящее MakeSound() .

Теперь, если вас интересует понимание того, как среда CLR определяет, какой метод вызывать, тогда я бы порекомендовал вам прочитать серию из трех частей Эрика Липперта на эту тему, начинающуюся здесь.