Правильный способ думать об этом - представить, что каждый класс требует, чтобы у его объектов было определенное количество «слотов»; эти слоты заполнены методами. Вопрос "какой метод на самом деле вызывается?" требует, чтобы вы выяснили две вещи:
- Каково содержимое каждого слота?
- Какой слот называется?
Давайте начнем с рассмотрения слотов. Есть два слота. Все экземпляры A должны иметь слот, который мы будем называть GetNameSlotA. Все экземпляры C должны иметь слот, который мы будем называть GetNameSlotC. Вот что означает «новый» для объявления в C - это означает «я хочу новый слот». По сравнению с «переопределить» в объявлении в B, что означает «Я не хочу новый слот, я хочу повторно использовать GetNameSlotA».
Конечно, C наследуется от A, поэтому C также должен иметь слот GetNameSlotA. Поэтому экземпляры C имеют два слота - GetNameSlotA и GetNameSlotC. Экземпляры A или B, которые не являются C, имеют один слот, GetNameSlotA.
Теперь, что входит в эти два слота, когда вы создаете новый C? Есть три метода, которые мы будем называть GetNameA, GetNameB и GetNameC.
Объявление A гласит «положить GetNameA в GetNameSlotA». A является суперклассом C, поэтому правило A применяется к C.
Объявление B гласит "положить GetNameB в GetNameSlotA". B является суперклассом C, поэтому правило B применяется к экземплярам C. Теперь у нас есть конфликт между A и B. B является более производным типом, поэтому он выигрывает - правило B переопределяет правило A. Отсюда и слово «переопределить» в объявлении.
Объявление C гласит: «Поместите GetNameC в GetNameSlotC».
Следовательно, у вашего нового C будет два слота. GetNameSlotA будет содержать GetNameB, а GetNameSlotC будет содержать GetNameC.
Теперь мы определили, какие методы в каких слотах, поэтому мы ответили на наш первый вопрос.
Теперь мы должны ответить на второй вопрос. Какой слот называется?
Думайте об этом, как будто вы компилятор. У вас есть переменная. Все, что вы знаете об этом, это то, что он относится к типу А. Вам предлагается разрешить вызов метода для этой переменной. Вы смотрите на слоты, доступные на A, и единственный слот, который вы можете найти, который соответствует, является GetNameSlotA. Вы не знаете о GetNameSlotC, потому что у вас есть только переменная типа A; почему вы ищете слоты, которые относятся только к C?
Следовательно, это вызов того, что находится в GetNameSlotA. Мы уже определили, что во время выполнения GetNameB будет в этом слоте. Следовательно, это вызов GetNameB.
Ключевым выводом здесь является то, что в разрешении перегрузки C # выбирает слот и генерирует вызов тому, что происходит в этом слоте.