#C

انواع Inheritance در #C

در این مقاله قصد داریم انواع Inheritance (وراثت) در سی شارپ و کاربرد آنها در سناریوهای مختلف را بیان کنیم.

Inheritance (وراثت) در سی شارپ چیست؟

Inheritance (وراثت) یکی از اصلی ترین پارادایم های برنامه نویسی شی گرا است و امکان استفاده مجدد از همان کد را در بیش از یک کلاس فراهم می کند. به این ترتیب، به جای بازنویسی یک کد در چندین مکان، از Inheritance (وراثت) برای تعریف یک کلاس بر اساس کلاس دیگری که قبلا تعریف شده است استفاده می کنیم. ما از یک base class (کلاس پایه) برای ایجاد کلاس های derived ( مشتق شده) استفاده می کنیم.

در این مقاله به انواع مختلف  Inheritance (وراثت) می پردازیم. 3 نوع Inheritance (وراثت) در سی شارپ وجود دارد :

  1. Single Inheritance (وراثت واحد)
  2. Multilevel Inheritance (وراثت چند سطحی)
  3. Multiple Inheritance (وراثت چندگانه)

برای رسیدن به اصل موضوع، اجازه دهید یک base class (کلاس پایه) تعریف کنیم:

public class MobileDevice
{
    public string OperatingSystem { get; set; } = null!;
    public double Inches { get; set; }
    public bool IsConnected { get; set; }
    public virtual bool DeviceCanMakePhoneCall()
    {
       return false;
    }
}

MobileDevice کلاسی با سه ویژگی است OperatingSystem ،Inches و IsConnected و متد ()DeviceCanMakePhoneCall خروجی false را پیاده سازی می کند. کلمه کلیدی virtual مجوز به لغو این روش در کلاس های derived ( مشتق شده) آن را ایجاد میکند.

حال، بیایید با جزئیات بیشتر به هر نوع  ارث بری در سی شارپ بپردازیم.

1- Single Inheritance (وراثت واحد)

 ساده ترین نوع ارث، Single Inheritance است.

بیایید یک Smartphone کلاس جدید معرفی کنیم:

public class Smartphone : MobileDevice
{
    public List<string> InstalledApps { get; private set; }
    public Smartphone() 
    {
        InstalledApps = new List<string>();
    }
    protected Smartphone(string operatingSystem)
    {
        OperatingSystem = operatingSystem;
        InstalledApps = new List<string>();
    }
    public override bool DeviceCanMakePhoneCall()
    {
        return true;
    }
    public virtual void GetDescription()
    {
        Console.WriteLine($"This smartphone is {Inches} inches big 
and its operating system is {OperatingSystem}");
    }
    public void ShowInstalledApps()
    {
        Console.WriteLine($"There are {InstalledApps.Count} app installed");
    }

}

Smartphone از MobileDevice ارث می برد  و متد ()DeviceCanMakePhoneCall را باطل می کند و true برمی گرداند .

اکنون می توانیم یک نمونه از کلاس Smartphone تعریف کنیم:

var smartphone = new Smartphone()
{
    Inches = 5.5,
    OperatingSystem = "iOS"
};

مجموعه Inches و OperatingSystem خواص به ارث رسیده از MobileDevice.متد ()GetDescription نیز virtual است که همه کلاس های derived ( مشتق شده) میتوانند عملکرد متد را تغییر دهند. علاوه بر این، ما متد ()ShowInstalledApp اضافه کرده ایم  که تعداد برنامه های نصب شده را چاپ می کند. و virtual نیست این بدان معناست که هر کلاسی که از Smartphone به ارث برده شود، پیاده سازی یکسانی از این متد خواهد داشت.

اگر متدهای ()smartphone.GetDescription و ()smartphone.ShowInstalledApps را فراخوانی کنیم، مقادیری را که برای نمونه خود تنظیم کرده ایم دریافت می کنیم Smartphone:

This smartphone is 5,5 inches big and its operating system is iOS There are 0 app installed

در نهایت، ویژگی InstalledApps فهرستی از برنامه های دانلود شده در دستگاه را نشان می دهد. در واقع، کلاس derived ( مشتق شده) می‌تواند ویژگی‌های خاص خود را غیر از آنهایی که از base class به ارث برده است، داشته باشد:

2- Multilevel Inheritance (وراثت چند سطحی)

وراثت چندسطحی اجازه می دهد که یک کلاس از کلاس دیگری که قبلاً از یک base class (کلاس پایه) derived ( مشتق شده) است، استخراج شود. به عنوان مثال، بیایید کلاسAndroidSmartphone را ایجاد کنیم:

public class AndroidSmartphone : Smartphone
{
    public AndroidSmartphone() : base(operatingSystem: "Android")
    {
    }
    public override void GetDescription()
    {
        Console.WriteLine($"This Android smarphone is {Inches} inches big, 
and {InstalledApps.Count} apps downloaded from Google Store");
    }
    public void DownloadAppFromStore(string app)
    {
        InstalledApps.Add(app);
        Console.WriteLine($"I downloaded {app} from Google Store");
    }
}



کلاس AndroidSmartphone خصوصیات را از کلاس Smartphone به  ارث می برد اما مقدار OperatingSystem همیشه برابر با "Android" است. در واقع، در این حالت، آن را در سازنده تعریف می کنیم،

بنابراین وقتی یک نمونه جدید از AndroidSmartphone را تعریف می کنیم ، نیازی به تنظیم دستی آن نداریم.

AndroidSmartphone متد ()GetDescription را که در آن کلاس (Smartphone) virtual بود تغییر می دهد.همانن طور که با ویژگی‌ها، کلاس‌های مشتق‌شده می‌توانند متدهای منحصربه‌فردی مانند DownloadAppFromStore(string app ایجاد کنند.

برای ادامه، بیایید یک AndroidSmartphone نمونه ایجاد کنیم و متدها را فراخوانی کنیم:

var androidSmartphone = new AndroidSmartphone()
{
    Inches = 4,
};
androidSmartphone.GetDescription();
androidSmartphone.DownloadAppFromStore("WhatsApp");
androidSmartphone.ShowInstalledApps();
Console.WriteLine(androidSmartphone.OperatingSystem);
Console.WriteLine(androidSmartphone is MobileDevice);

از آنجایی که ما از وراثت چند سطحی استفاده می کنیم،عبارت زیر را چاپ می کند:

This Android smarphone is 4 inches big, and 0 apps downloaded from Google Store
I downloaded WhatsApp from Google Store
There are 1 app installed
Android
True

در نهایت، خروجی از ()GetDescription با خروجی اجرا شده در کلاس Smartphone متفاوت است، زیرا ما آن را override می کنیم:

 

3- Multiple Inheritance (وراثت چندگانه)

سی شارپ دقیقاً مفهومی از وراثت چندگانه ندارد ، که به چندین کلاس اجازه می دهد از یک base class واحد  ارث بری کنند. 

برای نشان دادن اینکه چگونه می توانیم این مشکل را حل کنیم، ابتدا یک ConvertibleNotebook کلاس ایجاد می کنیم:

public class ConvertibleNotebook : MobileDevice
{
    public int UsbPortNumbers { get; set; }
    public override bool DeviceCanMakePhoneCall()
    {
        if (IsConnected)
        {
            return true;
        }
        return false;
    }
    public void WriteWithKeyboard(string message)
    {
        Console.WriteLine(message);
    }
}

یک ConvertibleNotebook از همه ویژگی ها و overrides متد ()DeviceCanMakePhoneCall کلاس MobileDevice ارث می برد. علاوه بر این، دارای ویژگی UsbPortNumbers  و متد WriteWithKeyboard(string message) است که پیام ارسال شده به عنوان پارامتر را روی کنسول چاپ می کند.

اساسا، Smartphone و ConvertibleNotebook کلاس ها متدها و ویژگی های مشترک تعریف شده توسط MobileDevice کلاس را دارند، اما آنها همچنین دارای ویژگی ها و متدهای خاص خود هستند که آنها را به طور خاص تعریف می کنند.

بیایید یک نمونه از کلاس ConvertibleNotebook ایجاد کنیم:

var convertibleNotebook = new ConvertibleNotebook()
{
    Inches = 11.6,
    OperatingSystem = "Windows 11",
    UsbPortNumbers = 3
};
convertibleNotebook.DeviceCanMakePhoneCall();
convertibleNotebook.WriteWithKeyboard("I can write with keyboard");

یک ConvertibleNotebook از MobileDevice ارث می برد و همچنین دارای این ویژگی ها نیز است: Inches, OperatingSystem, UsbPortNumbers

 

 

C# اجازه ارث بردن از بیش از یک base class (کلاس پایه) را نمی دهد . این عمدتاً به این دلیل است که این امر پیچیدگی بیش از حد را، بدون سود زیاد، اضافه می کند. ( برای اطلاعات بیشتر به  این  لینک مراجعه کنید )

در نظر بگیریم ConvertibleNotebook از آنجایی که قبلاً MobileDevice کلاس را به ارث می برد، نمی تواند کلاس دیگری را به  ارث ببرد.ارث بردن از کلاس دیگر منجر به خطای کامپایلر می شود.

ما می توانیم با استفاده از یک interfaces (رابط) بر این مشکل غلبه کنیم:

public interface ILaptop
{
    public int UsbPortNumbers { get; set; }
    public void WriteWithKeyboard(string message);
}

اکنون کلاس ConvertibleNotebook می تواند هم از class و هم از interfaces ارث بری کند: 

public class ConvertibleNotebook : MobileDevice, ILaptop
{
    public int UsbPortNumbers { get; set; }
    public override bool DeviceCanMakePhoneCall()
    {
        if (IsConnected)
        {
           return true;
        }
 
        return false;       
    }
    public void WriteWithKeyboard(string message)
    {
        Console.WriteLine(message);
    }
}

به این ترتیب، ILaptop interface را کلاس  ConvertibleNotebook پیاده سازی می کند که یک پیاده سازی از WriteWithKeyboard(string message) را ارائه می دهد، اما از MobileDevice کلاس نیز ارث می برد و متد ()DeviceCanMakeCall را override میکند.

 

چگونه از Inheritance (وراثت) در سی شارپ جلوگیری کنیم

می دانیم که همه متدها (یا خصوصیات) یک کلاس نباید در کلاس های derived ( مشتق شده) قابل مشاهده باشند. برای انجام این کار، برای متدهای overrid شده، مانند متد ()GetDescription در کلاس AndroidSmartphone، باید از کلمه کلیدی sealed استفاده کنیم:

public override sealed void GetDescription()
{
    Console.WriteLine($"This Android smarphone is {Inches} inches big, 
and {InstalledApps.Count} apps downloaded from Google Store");
}

به این ترتیب، هر کلاس derived ( مشتق شده) دیگری نمی تواند این متد را به ارث ببرد.هنگامی که ما با ویژگی یا با یک متد not-overridden سروکار داریم، در عوض، باید از private استفاده کنیم. به عنوان مثال، می توانیم در Smartphone کلاس اصلاح کنیم:

private void ShowInstalledApps()
{
    Console.WriteLine($"There are {InstalledApps.Count} app installed");
}

در این صورت، اگر بخواهیم آن را در AndroidSmarphone نمونه فراخوانی کنیم، با خطای کامپایل مواجه خواهیم شد. در واقع متد ()ShowInstalledApps در کلاس (Smartphone) private است و در کلاس AndroidSmartphone غیر قابل مشاهده است.

androidSmartphone.ShowInstalledApps();
 //Error CS0122 'Smartphone.ShowInstalledApps()' is inaccessible due to its protection level<br> 

همچنین می‌توانیم کل کلاس را sealed (مهر و موم) کنیم.

نتیجه :

در این مقاله به انواع مختلف ارث بری در سی شارپ پرداختیم و نشان دادیم که چگونه می توانیم از آن استفاده کنیم یا از آن در برنامه های خود جلوگیری کنیم.