مقدمه

وارد فصل ششم و یکی دیگر از ارکان اصلی برنامه‌نویسی شیءگرا (OOP) یعنی وراثت (Inheritance) می‌شویم. وراثت مکانیزمی است که به یک کلاس اجازه می‌دهد تا ویژگی‌ها (پراپرتی‌ها) و رفتارها (متدها) را از کلاس دیگری به ارث ببرد. این قابلیت، استفاده مجدد از کد را به شدت افزایش داده و به ما کمک می‌کند تا یک سلسله مراتب منطقی و طبیعی بین انواع داده‌ی خود ایجاد کنیم. در این مدل، کلاسی که ویژگی‌های خود را به اشتراک می‌گذارد، کلاس پایه (Base Class) یا والد نامیده می‌شود و کلاسی که این ویژگی‌ها را به ارث می‌برد، کلاس مشتق‌شده (Derived Class) یا فرزند نام دارد.

رابطه‌ی "Is-A" (یک نوع از)

وراثت برای مدل‌سازی رابطه‌ی "Is-A" یا "یک نوع از" به کار می‌رود. این یک آزمون ساده برای تشخیص این است که آیا استفاده از وراثت در یک سناریو منطقی است یا خیر. برای مثال:

  • یک Dog یک نوع Animal است.
  • یک Car یک نوع Vehicle است.
  • یک Manager یک نوع Employee است.

در تمام این موارد، کلاس مشتق‌شده (فرزند) یک نسخه‌ی تخصصی‌تر از کلاس پایه (والد) است. او تمام ویژگی‌های عمومی والد را دارد، به علاوه‌ی ویژگی‌ها و رفتارهای مخصوص به خود.

تعریف کلاس‌های پایه و مشتق‌شده

برای پیاده‌سازی وراثت در C#، پس از نام کلاس مشتق‌شده، از یک علامت دو نقطه (`:`) و سپس نام کلاس پایه استفاده می‌کنیم. بیایید این مفهوم را با یک مثال کلاسیک از حیوانات پیاده‌سازی کنیم.

Copy Icon Program.cs
// 1. The Base Class (Parent)
public class Animal
{
    public string Name { get; set; }

    public void Eat()
    {
        Console.WriteLine($"{Name} is eating.");
    }
}

// 2. The Derived Class (Child)
// Dog inherits from Animal
public class Dog : Animal
{
    // Dog-specific method
    public void Bark()
    {
        Console.WriteLine("Woof woof!");
    }
}

// Another Derived Class
public class Cat : Animal
{
    // Cat-specific method
    public void Meow()
    {
        Console.WriteLine("Meow!");
    }
}

در کد بالا، Animal کلاس پایه است. کلاس‌های Dog و Cat هر دو از Animal ارث‌بری می‌کنند. این بدان معناست که هر شیء از نوع Dog یا Cat به طور خودکار دارای پراپرتی Name و متد Eat خواهد بود.

استفاده مجدد از اعضای کلاس پایه

اکنون که وراثت را برقرار کردیم، می‌توانیم ببینیم که چگونه کلاس‌های مشتق‌شده از اعضای کلاس پایه استفاده می‌کنند.

Copy Icon Program.cs
Dog myDog = new Dog();
myDog.Name = "Buddy"; // Accessing the 'Name' property inherited from Animal
myDog.Eat();       // Calling the 'Eat' method inherited from Animal
myDog.Bark();      // Calling the 'Bark' method specific to Dog

Cat myCat = new Cat();
myCat.Name = "Whiskers";
myCat.Eat();
myCat.Meow();

خروجی این کد به شکل زیر خواهد بود:

Buddy is eating.
Woof woof!
Whiskers is eating.
Meow!
                    

همانطور که می‌بینید، ما بدون نیاز به تعریف مجدد Name یا Eat در کلاس‌های Dog و Cat، توانستیم از آن‌ها استفاده کنیم. این قدرت استفاده مجدد از کد است که وراثت برای ما به ارمغان می‌آورد.

تخصصی کردن رفتار با virtual و override

وراثت فقط برای استفاده مجدد از کد نیست؛ بلکه برای تخصصی کردن رفتار نیز به کار می‌رود. ممکن است بخواهیم یک متد در کلاس پایه وجود داشته باشد، اما هر کلاس مشتق‌شده پیاده‌سازی مخصوص به خود را برای آن ارائه دهد. برای مثال، همه‌ی حیوانات صدا تولید می‌کنند، اما صدای هر کدام متفاوت است. برای این کار از کلمات کلیدی virtual و override استفاده می‌کنیم.

  • virtual: وقتی یک متد را در کلاس پایه با این کلمه‌ی کلیدی مشخص می‌کنیم، به کلاس‌های مشتق‌شده اجازه می‌دهیم که در صورت تمایل، آن را بازنویسی (override) کنند.
  • override: وقتی یک متد را در کلاس مشتق‌شده با این کلمه‌ی کلیدی مشخص می‌کنیم، یعنی در حال ارائه‌ی یک پیاده‌سازی جدید برای متد virtual کلاس پایه هستیم.
Copy Icon Program.cs
public class Animal
{
    public string Name { get; set; }
    
    // This method can be overridden by derived classes.
    public virtual void MakeSound()
    {
        Console.WriteLine("The animal makes a sound.");
    }
}

public class Dog : Animal
{
    // Overriding the base class method.
    public override void MakeSound()
    {
        Console.WriteLine("Woof woof!");
    }
}

// --- Usage ---
Animal genericAnimal = new Animal();
Dog myDog = new Dog();

genericAnimal.MakeSound(); // Output: The animal makes a sound.
myDog.MakeSound();       // Output: Woof woof!

در این مثال، متد MakeSound در کلاس Animal به صورت virtual تعریف شده است. کلاس Dog این متد را override کرده و پیاده‌سازی مخصوص به خود را ارائه داده است. وقتی myDog.MakeSound() را فراخوانی می‌کنیم، نسخه‌ی بازنویسی شده در کلاس Dog اجرا می‌شود. این مفهوم اساس چندریختی (Polymorphism) را تشکیل می‌دهد که در درس‌های آینده به آن خواهیم پرداخت.

فراخوانی سازنده‌ی کلاس پایه با base

وقتی یک شیء از کلاس مشتق‌شده ایجاد می‌شود، سازنده‌ی آن به طور خودکار یکی از سازنده‌های کلاس پایه را فراخوانی می‌کند تا بخش پایه‌ی شیء نیز مقداردهی اولیه شود. اگر به طور صریح مشخص نکنیم، سازنده‌ی پیش‌فرض (بدون پارامتر) کلاس پایه فراخوانی می‌شود. برای فراخوانی یک سازنده‌ی خاص از کلاس پایه، از کلمه‌ی کلیدی base استفاده می‌کنیم.

Copy Icon Program.cs
public class Animal
{
    public string Name { get; } // Let's make it readonly after construction.

    // Base class constructor.
    public Animal(string name)
    {
        this.Name = name;
        Console.WriteLine("Animal constructor called.");
    }
}

public class Dog : Animal
{
    // Derived class constructor calling the base class constructor.
    public Dog(string name) : base(name)
    {
        Console.WriteLine("Dog constructor called.");
    }
}

// --- Usage ---
Dog myDog = new Dog("Rex");
Console.WriteLine($"The dog's name is {myDog.Name}.");

خروجی این کد ترتیب فراخوانی سازنده‌ها را نشان می‌دهد:

Animal constructor called.
Dog constructor called.
The dog's name is Rex.
                    

سینتکس : base(name) به کامپایلر می‌گوید که قبل از اجرای بدنه‌ی سازنده‌ی Dog، ابتدا سازنده‌ی کلاس Animal را با آرگومان name فراخوانی کند. این کار تضمین می‌کند که بخش پایه‌ی شیء به درستی مقداردهی اولیه شده است.