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

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

Статический полиморфизм

Механизм связывания функции с объектом во время компиляции называется ранним связыванием. Он также называется статической привязкой. C # предоставляет два метода для реализации статического полиморфизма. Они -

  • Перегрузка функций
  • Перегрузка оператора

Мы обсудим перегрузку оператора в следующей главе.

Перегрузка функции

Вы можете иметь несколько определений для одного и того же имени функции в той же области. Определение функции должно отличаться друг от друга по типам и / или количеству аргументов в списке аргументов. Вы не можете перегружать объявления функций, которые отличаются только возвращаемым типом.

В следующем примере показано использование функции print () для печати различных типов данных:

using System;

namespace PolymorphismApplication {
   class Printdata {
      void print(int i) {
         Console.WriteLine("Printing int: {0}", i );
      }
      void print(double f) {
         Console.WriteLine("Printing float: {0}" , f);
      }
      void print(string s) {
         Console.WriteLine("Printing string: {0}", s);
      }
      static void Main(string[] args) {
         Printdata p = new Printdata();
         
         // Call print to print integer
         p.print(5);
         
         // Call print to print float
         p.print(500.263);
         
         // Call print to print string
         p.print("Hello C++");
         Console.ReadKey();
      }
   }
}

Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

Printing int: 5
Printing float: 500.263
Printing string: Hello C++

Динамический полиморфизм

C # позволяет создавать абстрактные классы, которые используются для обеспечения частичной реализации класса интерфейса. Реализация завершается, когда производный класс наследуется от него. Абстрактные классы содержат абстрактные методы, которые реализуются производным классом. Производные классы имеют более специализированную функциональность.

Вот правила об абстрактных классах:

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

Следующая программа демонстрирует абстрактный класс:

using System;

namespace PolymorphismApplication {
   abstract class Shape {
      public abstract int area();
   }
   
   class Rectangle:  Shape {
      private int length;
      private int width;
      
      public Rectangle( int a = 0, int b = 0) {
         length = a;
         width = b;
      }
      public override int area () { 
         Console.WriteLine("Rectangle class area :");
         return (width * length); 
      }
   }
   class RectangleTester {
      static void Main(string[] args) {
         Rectangle r = new Rectangle(10, 7);
         double a = r.area();
         Console.WriteLine("Area: {0}",a);
         Console.ReadKey();
      }
   }
}

Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

Rectangle class area :
Area: 70

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

Следующая программа демонстрирует это:

using System;

namespace PolymorphismApplication {
   class Shape {
      protected int width, height;
      
      public Shape( int a = 0, int b = 0) {
         width = a;
         height = b;
      }
      public virtual int area() {
         Console.WriteLine("Parent class area :");
         return 0;
      }
   }
   class Rectangle: Shape {
      public Rectangle( int a = 0, int b = 0): base(a, b) {

      }
      public override int area () {
         Console.WriteLine("Rectangle class area :");
         return (width * height); 
      }
   }
   class Triangle: Shape {
      public Triangle(int a = 0, int b = 0): base(a, b) {
      }
      public override int area() {
         Console.WriteLine("Triangle class area :");
         return (width * height / 2); 
      }
   }
   class Caller {
      public void CallArea(Shape sh) {
         int a;
         a = sh.area();
         Console.WriteLine("Area: {0}", a);
      }
   }  
   class Tester {
      static void Main(string[] args) {
         Caller c = new Caller();
         Rectangle r = new Rectangle(10, 7);
         Triangle t = new Triangle(10, 5);
         
         c.CallArea(r);
         c.CallArea(t);
         Console.ReadKey();
      }
   }
}

Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

Rectangle class area:
Area: 70
Triangle class area:
Area: 25