Объектно-ориентированное программирование — это кроличья нора, где полиморфизм — всего лишь еще одна нить в яме, давайте посмотрим, насколько глубоко она может зайти.

Эта статья отправит вас в путешествие, чтобы разгадать тайну полиморфизма времени компиляции. Кроме того, в C# у нас есть как раз нужный материал, чтобы сделать его хуже, с перегрузкой и переопределением методов, у нас также есть концепция сокрытия методов, что делает всю идею полиморфизма немного непонятной. И именно поэтому я собираюсь устранить путаницу, разбив идею на 3 части.

Это серия из 3 статей, в которых мы собираемся глубоко погрузиться в полиморфизм. Ниже приведены 3 части головоломки.

  1. Полиморфизм времени компиляции
  2. Полиморфизм во время выполнения
  3. Метод скрытия/затенения

Почему это называется полиморфизмом времени компиляции?

Потому что компилятор знает, какой метод вызывать во время компиляции, основываясь на сигнатуре метода. Эта зависимость решается во время компиляции. Хорошо! это звучит хорошо в теории, но давайте посмотрим, как это работает на практике.

ДЕРЖИТЕ СЕК!!, что вы имели в виду под сигнатурой метода?

Ну там 4 штуки на любой способ.

  1. Название метода,
  2. Тип возврата,
  3. Параметр/с,
  4. Спецификатор доступа

Между сообществом разработчиков идет огромный спор о том, должен ли возвращаемый тип быть частью сигнатуры метода или нет!!

Как насчет того, чтобы убрать эту путаницу с Майкрософт?

Согласно Microsoft, «возвращаемый тип метода не является частью сигнатуры метода для целей перегрузки метода. Однако он является частью сигнатуры метода при определении совместимости между делегатом и методом, на который он указывает».

Проще говоря, при «переопределении метода» тип возвращаемого значения учитывается как часть сигнатуры метода.

при «перегрузке метода» он просто игнорируется.

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

Так что сделайте глубокий вдох и присоединяйтесь ко мне в этом путешествии.

Есть несколько правил, о которых нужно знать при перегрузке метода.

  1. Type параметров должны быть разными, и имя параметра не имеет значения.
  2. Количество параметров должно быть разным.
  3. Порядок параметров должен быть другим.
  4. И последнее, но не менее важное: return-type, access specifiers или любые другие причудливые ключевые слова, такие как static, abstract, sealed, virtual, не учитываются.

Чтобы продемонстрировать эти правила, я буду кодировать UML на протяжении всей статьи, это затронет почти все аспекты перегрузки методов.

Давайте создадим костюмы для некоторых из наших любимых супергероев?

Представьте, что мы разрабатываем class superhero, в котором наша задача — настроить костюм. В игру могут входить различные аргументы, цвет костюма, тип костюма, нравится ли герою носить свое нижнее белье или нет и т. д. Давайте продолжим и создадим класс для выполнения этих пунктов.

В следующем фрагменте кода мы разрабатываем костюм для «Супермена».

Этот код работает до тех пор, пока вы хотите, чтобы ваш супергерой по умолчанию был суперменом. Но давайте посмотрим правде в глаза, он не так хорош. Итак, чтобы настроить костюм для другого супергероя, мы можем создать отдельный method CustomizeSuit().

Скажем, у меня есть typeOfSuit, и я хочу настроить его, это может быть кожаный или металлический костюм. Чтобы закодировать это, я могу просто создать метод, который будет принимать один параметр string type, который будет typeOfSuit.

Правило №1: тип параметра/ов должен быть другим, название параметра/ов не имеет значения.

Мой супергерой проявляет интерес к металлическому костюму, теперь он явно не может носить поверх него нижнее белье. Поэтому я могу просто перегрузить CustomizeSuit() method, чтобы принять другой тип параметра. Здесь мы перегружаем метод, который принимает boolean type, если значение этого параметра true, то наш герой будет носить нижнее белье снаружи или внутри.

Правило 2. Количество параметров должно быть разным.

Некоторые пользователи хотят либо настроить тип костюма, либо решить, где разместить нижнее белье, но другие могут быть заинтересованы в том, чтобы изменить и то, и другое вместе. Мы можем добиться этого, просто перегрузив метод другим количеством параметров.

Правило №3: порядок параметров должен быть другим.

У вас может быть одинаковое количество параметров, но порядок должен быть другим, например, я могу взять код из listing 4 и сделать это.

Пояснение к листингу 5:я просто менял порядок параметров в листинге 5. Компилятор ищет индекс параметра и его тип. Если Type отличается по тому же индексу, то это допустимый перегруженный метод.

Другим вариантом может быть листинг 6.

Правило № 4. И последнее, но не менее важное: тип возвращаемого значения, спецификаторы доступа или любые другие причудливые ключевые слова, такие как статический, абстрактный, запечатанный, виртуальный, ничего не значат.

Давайте сойдем с ума по этому поводу, не так ли?

Правило 4.1. Тип возвращаемого значения не принимается во внимание.

В следующем примере между двумя методами все одинаково, за исключением return-type. Если у вас есть 2 метода с одинаковой сигнатурой, но разными return-type, это не считается перегрузкой метода. Компилятор выдаст исключение времени компиляции.

Исключение:тип ‘SuperHero’ уже определяет элемент с именем ‘CustomizeSuit’ с теми же типами параметров.

Правило 4.2. Спецификаторы доступа игнорируются.

Что ж, правило 4.1 было довольно крутым. Теперь для правила номер 4.2 нам нужно изменить спецификаторы доступа.

То же хорошее исключение!!

Правило 4.3. Специальные ключевые слова игнорируются.

В следующем снимке вы увидите, что ни одно из ключевых слов не работает с перегруженным методом. Таким образом, украшение метода этими ключевыми словами ничего не значит для концепции перегрузки.

Примечание. Для абстрактного метода я пометил класс как абстрактный.

Прежде всего методы подпадают под одно и то же исключение.

Тип ‘SuperHero’ уже определяет элемент с именем ‘CustomizeSuit’ с теми же типами параметров.

Мы прошли долгий путь с перегрузкой методов, есть еще одна важная концепция, которую я хотел бы добавить в качестве бонуса.

Перегрузка метода с наследованием

Следующий фрагмент кода является родительским class SuperHero. Он содержит все перегруженные методы, которые мы обсуждали до сих пор.

Создадим дочерний класс, который наследует родительский class SuperHero.

За class IronMan следует ребенок class SuperHero. который очень элегантно перегружает CustomizeSuit() method тремя параметрами.

Ага!! Вы не ослышались, перегрузка методов не ограничена внутри одного класса.

Я создал экземпляр как родительского, так и дочернего класса. Взгляните на следующее изображение 5, я получаю 4 перегруженных метода с родительским объектом.

Давайте посмотрим, какое число мы получим, когда попытаемся вызвать CustomizeSuit() с дочерним объектом. Как вы можете видеть на изображении 6, дочерний класс A.K.A. IronMan имеет еще один дополнительный счет, который относится к методу, который мы только что создали в листинге 10. Таким образом, доказано, что вы действительно можете перегрузить метод из разных классов.

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

Примечание. Четыре упомянутых выше правила применимы только к одному и тому же классу.

Давайте не будем путать здесь! Позвольте мне продемонстрировать это на примере. Посмотрите на следующие два изображения 7 и 8.

На изображении ниже CustomizeSuit() method определен в class SuperHero, и тот же метод снова определен в class IronMan, и кажется, что у компилятора нет проблем с этим.

Объект class SuperHero будет вызывать собственную версию CustomizeSuit() method, а объект class IronMan будет иметь дополнительный перегруженный метод count, но все равно будет вызывать свою собственную реализацию CustomizeSuit() method.

Итак, у вас может быть одна и та же сигнатура перегруженного метода, только если вы находитесь в другом типе.

Но у вас не может быть одной и той же подписи в одном и том же типе.

Посмотрите, как реагирует компилятор на изображении 8. У меня выдает ошибку, когда я перегружаю метод с той же сигнатурой внутри одного и того же class. Так что помните, что эти четыре правила применимы в пределах одного и того же class.

Заключение

Да, перегрузка методов может быть довольно сложной, когда мы пытаемся охватить все возможности, но как только вы уясните основы, вы сможете понять ее назначение в объектно-ориентированном программировании. В этой статье мы подробно рассмотрели все аспекты с различными примерами, чтобы понять, что приемлемо, а что нет, когда речь идет о полиморфизме времени компиляции.

Want to Connect?
Hit me up on LinkedIn.