Polymorphism เป็นแนวคิดพื้นฐานในการเขียนโปรแกรมเชิงวัตถุที่ช่วยให้วัตถุประเภทต่างๆ ถือเป็นวัตถุประเภทฐานทั่วไป ช่วยให้อินเทอร์เฟซเดียวเพื่อแสดงการใช้งานที่เป็นรูปธรรมหลายอย่าง โดยให้ความยืดหยุ่นและความสามารถในการขยายในการออกแบบโค้ด ในบทความนี้ เราจะสำรวจแนวคิดเรื่องความหลากหลายใน C# ผ่านตัวอย่างต่างๆ เพื่อทำความเข้าใจการประยุกต์ใช้ในทางปฏิบัติได้ดียิ่งขึ้น

1. ความแตกต่างกับมรดก:

โดยทั่วไปความหลากหลายจะเกิดขึ้นได้จากการสืบทอด โดยที่คลาสที่ได้รับมาสามารถใช้เป็นอินสแตนซ์ของคลาสพื้นฐานได้ ซึ่งช่วยให้อินเทอร์เฟซที่สอดคล้องกันสำหรับวัตถุประเภทต่างๆ มาอธิบายสิ่งนี้ด้วยตัวอย่าง:

using System;

// Base class representing a Shape
class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Drawing a shape.");
    }
}

// Derived classes Circle and Rectangle inheriting from Shape
class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle.");
    }
}

class Rectangle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a rectangle.");
    }
}

class Program
{
    static void Main()
    {
        Shape shape1 = new Circle();
        Shape shape2 = new Rectangle();

        shape1.Draw(); // Output: Drawing a circle.
        shape2.Draw(); // Output: Drawing a rectangle.
    }
}

ในตัวอย่างนี้ เรามีคลาสพื้นฐาน Shape ที่มีเมธอดเสมือน Draw() ซึ่งจากนั้นจะถูกแทนที่โดยคลาส Circle และ Rectangle เมธอด Draw() ถูกเรียกใช้บนออบเจ็กต์ประเภทพื้นฐาน Shape และดำเนินการเมธอดแทนที่ที่เหมาะสมจากคลาสที่ได้รับตามประเภทรันไทม์ของออบเจ็กต์ สิ่งนี้เรียกว่าความหลากหลายแบบไดนามิกหรือความหลากหลายแบบรันไทม์

2. ความแตกต่างกับอินเทอร์เฟซ:

อินเทอร์เฟซยังมีบทบาทสำคัญในการบรรลุความหลากหลายโดยจัดทำสัญญาที่หลายคลาสสามารถนำมาใช้ได้ มาสำรวจตัวอย่างด้วยอินเทอร์เฟซ:

using System;

// Interface representing a Printable object
public interface IPrintable
{
    void Print();
}

// Classes Document and Image implementing IPrintable
class Document : IPrintable
{
    public void Print()
    {
        Console.WriteLine("Printing a document.");
    }
}

class Image : IPrintable
{
    public void Print()
    {
        Console.WriteLine("Printing an image.");
    }
}

class Program
{
    static void Main()
    {
        IPrintable printable1 = new Document();
        IPrintable printable2 = new Image();

        printable1.Print(); // Output: Printing a document.
        printable2.Print(); // Output: Printing an image.
    }
}

ในตัวอย่างนี้ เรามีอินเทอร์เฟซ IPrintable พร้อมเมธอด Print() ซึ่งใช้งานโดยคลาส Document และ Image เราสร้างอินสแตนซ์ของคลาสเหล่านี้และเก็บไว้ในตัวแปรประเภท IPrintable เมื่อเราเรียกใช้เมธอด Print() กับตัวแปรเหล่านี้ มันจะดำเนินการใช้งานที่เหมาะสมที่ได้รับจากคลาสที่เป็นรูปธรรม

3. ความแตกต่างด้วยวิธีโอเวอร์โหลด:

ความหลากหลายยังเกิดขึ้นได้จากการโอเวอร์โหลดวิธีการ ซึ่งมีการกำหนดวิธีการหลายวิธีที่มีชื่อเดียวกันแต่มีพารามิเตอร์ต่างกัน ลองดูตัวอย่าง:

using System;

class MathOperations
{
    public int Add(int num1, int num2)
    {
        return num1 + num2;
    }

    public double Add(double num1, double num2)
    {
        return num1 + num2;
    }
}

class Program
{
    static void Main()
    {
        MathOperations math = new MathOperations();
        int result1 = math.Add(5, 10); // Output: 15
        double result2 = math.Add(3.5, 2.5); // Output: 6.0

        Console.WriteLine("Result 1: " + result1);
        Console.WriteLine("Result 2: " + result2);
    }
}

ในตัวอย่างนี้ คลาส MathOperations กำหนดสองวิธีด้วยชื่อเดียวกัน Add แต่มีประเภทพารามิเตอร์ต่างกัน (จำนวนเต็มและสองเท่า) เมื่อเราเรียกใช้เมธอด Add ด้วยประเภทพารามิเตอร์ที่แตกต่างกัน วิธีการโอเวอร์โหลดที่เหมาะสมจะถูกเรียกใช้ตามประเภทข้อมูลของอาร์กิวเมนต์ สิ่งนี้เรียกว่าความหลากหลายทางเวลาคอมไพล์หรือความหลากหลายแบบคงที่

สรุป: Polymorphism เป็นแนวคิดที่ทรงพลังที่ช่วยให้โค้ดสามารถนำกลับมาใช้ใหม่ได้ มีความยืดหยุ่น และขยายได้ในการเขียนโปรแกรมเชิงวัตถุ ด้วยการใช้ประโยชน์จากการสืบทอด อินเทอร์เฟซ และการโอเวอร์โหลดเมธอด C# ช่วยให้นักพัฒนาสามารถเขียนโค้ดที่สะอาด เป็นโมดูล และบำรุงรักษาได้ง่าย การทำความเข้าใจและการใช้ความหลากหลายอย่างมีประสิทธิภาพสามารถนำไปสู่แอปพลิเคชันที่แข็งแกร่งและปรับขนาดได้