معرفی مفاهیم virtual, override, abstract‌ و sealed در #C

09 فروردین 1398
درسنامه درس 12 از سری آموزش سی شارپ (C#)
csharp-all-classes

همانطور که مستحضر هستید کلمه‌ی Polymorphism به معنای چندریختی است. در برنامه‌نویسی شیءگرا پلی مورفیسم اغلب به عنوان یک تغییردهنده رفتار یا یک واسط چند متدی مطرح می‌شود.

در زبان برنامه‌نویسی #C پلی مورفیسم به سه شکل ممکن پیاده‌سازی می‌شود:

  • استفاده از متدهای virtual و override کردن آنها در کلاس فرزند
  • بهره‌گیری از متدهای abstract در کلاس والد
  • بهره‌گیری از قابلیت واسط‌ها یا Interfaceها ( واسط‌ها در فصول بعدی به تفصیل توضیح داده خواهند شد)

متدهای virtual

فرض کنید یک کلاس والد به نام Shape دارید که در آن متدی به نام Draw تعریف شده است. متد Draw وظیفه‌ی ترسیم یک شیء یا شکل را به عهده دارد. این متد در تمام کلاس‌هایی که از این کلاس مشتق می‌شوند قابل استفاده است. بنابراین کلاس Shape را به صورت زیر می‌نویسم:

public class Shape
{
    public void Draw()
    {
        Console.WriteLine("Drawing the shape!");
    }
}

حال باید کلاس‌های فرزند مرتبط با این کلاس را تعریف کنیم بنابراین سه کلاس به نام‌های Triangle و Rectangle و Circle را تعریف می‌کنیم که هر سه از کلاس والد یعنی Shape مشتق شده اند:

public class Rectangle : Shape
{
        
}

public class Triangle : Shape
{
        
}

public class Circle : Shape
{
        
}

هر سه کلاسی که در فوق تعریف کردیم می‌توانند از متد Draw استفاده کنند زیرا این متد در کلاس اصلی والد تعریف شده است. حال اگر شیءای بسازیم آنگاه:

var rect = new Rectangle();
var tri = new Triangle();
var circ = new Circle();

rect.Draw();
tri.Draw();
circ.Draw();

Console.ReadKey();

در نهایت خروجی دستورات به صورت زیر است:

Drawing the shape!
Drawing the shape!
Drawing the shape!

اما این خروجی مورد پسند نیست و باید برای هر کلاس یک شکل کشیده شود در نهایت برای رفع این مشکل متد موجود در کلاس والد را به صورت virtual تعریف کرده و آن را درون کلاس فرزند override می‌کنیم. بنابراین تغییراتی در کلاس والد داده و متد Draw را به صورت زیر تعریف می‌کنیم:

public virtual void Draw()
{
    Console.WriteLine("Drawing the shape!");
}

حال همین تغییرات را درون کلاس‌های فرزند انجام می‌دهیم:

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

حال خروجی دستورات فوق به صورت زیر اصلاح می‌شوند:

Drawing rectangle!
Drawing the shape!
Drawing the shape!

همچنین اگر از کلمه‌ی کلیدی base درون متد کلاس فرزند استفاده کنیم محتوا و متد کلاس پایه نمایش داده خواهد شد:

public override void Draw()
{
    base.Draw();
}

استفاده از روش virtual و override‌ تنها به متدها ختم نمی‌شود بلکه می‌توان برای خصوصیات یا Property ها نیز این کار را انجام داد:

public class Human
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public virtual string FullSpecification
    {
        get { return FirstName + " " + LastName; }
    }
}

public class Employee : Human
{
    public string JobPosition { get; set; }

    public override string FullSpecification
    {
        get { return base.FullSpecification + " with job position: " + JobPosition; }
    }
}

معرفی کلاس‌ها و متدهای abstract و sealed

هنگامیکه یک برنامه را می‌نویسیم و کلاس‌ها و اعضای آن را مشخص می‌کنیم باید برای استفاده از کلاس‌ها یک سری محدودیت بگذاریم. مثلا یک کلاس پایه داریم که این کلاس نباید توسط سایر کلاس ها مورد استفاده قرار بگیرد و تنها اعضای آن کلاس و کلاس هایی که از آن به ارث برده‌اند، توانایی دسترسی به متدها و اعضای آن را دارند یا مثلا کلاسی را ایجاد کرده‌ایم که با اعمال محدودیت‌هایی اجاره ایجاد کلاس فرزند از آن را نمی‌دهیم. برای اعمال همچین محدودیت‌هایی از کلاس‌ها یا متدهایی با فرم abstract یا sealed استفاده می‌شود.

کلاس‌ها و اعضاء abstract

به مثال قبلی باز می‌گردیم که یک کلاس والد به نام Shape داشتیم و از روی این کلاس سه فرزند ساخته بودیم:

public class Shape
{
    public void Draw()
    {
        Console.WriteLine("Drawing the shape!");
    }
}

public class Rectangle : Shape
{
         
}
 
public class Triangle : Shape
{
         
}
 
public class Circle : Shape
{
         
}

اگر به مثال بالا مراجعه کنید متوجه می‌شوید که کلاس Shape عملا برای ما کاربردی ندارد. به عبارت دیگر در طول برنامه‌ی اصلی از آن استفاده نشده است. یعنی باید محدودیتی اعمال کنیم که از روی کلاس Shape شیء‌ای ایجاد نشود. برای اینکار کافیست کلاس Shape را از نوع abstract تعریف کنیم. بنابراین طی یک تعریف کلی برای abstract‌ داریم:

اگر کلاسی به صورت abstract ایجاد شود، در طول برنامه نمی‌توان از روی آن شیءای ساخت.

به مثال زیر توجه کنید:

public abstract class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Drawing the shape!");
    }
}

حال اگر بخواهیم از روی کلاس Shape یک شیء ایجاد کنیم با خطای زیر مواجه می‌شویم:

 Cannot create an instance of the abstract class

یعنی نمی‌توان از روی کلاس‌هایی که به صورت abstract‌ تعریف شده‌اند شیءای ایجاد کرد.

اما کاربردهای بیشتری از کلاس abstract انتظار می‌رود. در کلاس‌های abstract‌ می‌توان به طور مشابه متدهایی را تعریف کرد که به صورت abstract‌ باشند. این متدها تنها شامل signature هستند یعنی بدنه‌ای نداشته و پس از تعریف آنها به علامت ; ختم می‌شوند.  درصورتیکه یک متد به صورت abstract تعریف شود بدین گونه است که حتما باید آن را جهت استفاده در طی برنامه یا کلاس دیگر override کنند. برای مثال یکبار دیگر کلاس Shape‌ را بازنویسی می‌کنیم:

public abstract class Shape
{
    public abstract void Draw();
}

همانطور که ملاحظه می‌کنید متدی به نام Draw‌ وجود دارد که بدنه‌ای ندارد. علت این امر تعریف این متد به صورت abstract است. حال اگر کلاس فرزندی از کلاس Draw به ارث ببرد باید همواره متد درون آن به صورت abstract قرار بگیرد. بنابراین داریم:

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

کلاس‌ها و اعضاء sealed

در بخش وراثت آموزش دادیم که همواره می‌توان یک زنجیره‌ی وراثت در اختیار داشت مثلا کلاس B از کلاس A و کلاس C از کلاس B مشتق شود. حال درنظر بگیرید که می‌خواهیم به نحوی این زنجیره‌ی وراثت را قطع کنیم. برای اینکار باید کلاس موردنظر را از نوع sealed تعریف کنیم. بنابراین در طی یک تعریف کلی داریم:

کلاس‌هایی که به صورت sealed مورد استفاده قرار می‌گیرند، باعث حذف زنجیره وراثت می‌شوند.

به مثال زیر توجه کنید:

public class A
{
        
}

public sealed class B : A
{
        
}

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

public class A
{
        
}

public sealed class B : A
{
        
}
public class C:B
{

}

آنگاه با خطای زیر روبه‌رو می‌شویم:

Cannot inherit from sealed class 'B'

بدین معنی‌ست که شما نمی‌توانید از یک کلاس که به صورت sealed‌ تعریف شده است ویژگی یا متدی را به ارث ببرید.

یکی دیگر از کاربردهای عبارت sealed جلوگیری از override کردن یک متد است. به مثال زیر توجه کنید:

public abstract class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Drawing the shape!");
    }
}

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

این مثال بدین صورت عمل می‌کند که اگر کلاسی از کلاس Rectangle مشتق شد، دیگر قابلیت override کردن متد Draw را نداشته باشد زیر در متد موجود در کلاس Rectangle، متد به صورت sealed تعریف شده است.

با مرور این فصل اطلاعات بسیار مفیدی در اختیار شما قرار می‌گیرد. شما با کلمات کلیدی virtual, override, abstract, sealed‌ به صورت کامل آشنا شده و نحوه‌ی اعمال محدودیت‌ها به یک کلاس را فرا گرفتید. در فصل بعدی به توضیح مفصل واسط‌ها (Interface) می‌پردازیم. با ما همراه باشید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش سی شارپ (C#) توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (10 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

علی
07 مرداد 1402
خیلی عالی و زیبا توضیح دادید. ممنون از شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

سارا
01 دی 1401
چقدر خوب توضیح دادید، بعبارتی ابتدا چالش را مطرح کردید و بعد آن را با موضوع مورد نظر حل کردید. با سپاس

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

فاطمه
07 تیر 1401
بسیااارر عاااالییی توضیح داده بودین، خیلی خوب این مبحث رو متوجه شدم، بسیار ممنونم

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
11 مرداد 1401
باعث افتخار ماست

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

رضا
19 خرداد 1401
سلام. من به تازگی با وب سایت شما اشنا شدم,واقعا مطالبتون عالیه,ینی انقدر خوب توضیح دادین و نگارش خوبی دارین که واقعا کم تر سایت فارسی زبان رو دیدم که در این سطح باشه.واقعا ممنون

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
11 مرداد 1401
از اینکه دیدگاه خودتون رو با ما درمیون گذاشتید و به ما انرژی می‌دید ممنونیم.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

Alicha
09 اسفند 1400
قبلا چند باری از سایت استفاده کرده بودم ولی خیلی کمک کننده نبود همچنین ترتیب آموزش هارو خیلی مفید نمی دیدم. اما این قسمت از آموزش فوق العاده گیرا و مفید بود؛ با تشکر.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
11 مرداد 1401
امیدواریم بتونیم بی از پیش رضایت شما رو جلب کنیم.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

soheila
10 بهمن 1400
بسیار عالی و قابل فهم بود. ممنون از شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
11 مرداد 1401
خیلی خوشحالیم که این مطلب برای شما مفید بود

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

Hamid
25 تیر 1400
سلام مطالب خیلی عالی بود قابل فهم بود فقط بقیه مباحث c# را اضافه کنید

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
11 مرداد 1401
تشکر، بله حتما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

فرشاد
12 شهریور 1399
بسیار کامل و با بیان شیوا و مثال توصیف کردید. ممنون برای وقتی که برای آموزش دانسته هاتون گذاشتید. همیشه سلامت و موفق باشید. سپاس

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
11 مرداد 1401
از دیدگاهتون انرژی می‌گیریم. شما هم موید و پیروز باشید.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

راضی
04 بهمن 1397
سلام خیلی عالی بود. چجوری میتونم به فصلهای قبل دسترسی داشته باشم

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
11 مرداد 1401
سلام. از قسمت پایین مقاله

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

محمد حسین
10 آبان 1397
بسیار خوب و عالی توضیح دادین متشکر

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
11 مرداد 1401
سپاس از شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.