منو

آشنایی با مفهوم Polymorphism(قسمت بیست و یکم)

آشنایی با مفهوم Polymorphism
لیست جلسات
DatoosTech img Course

قسمت پنجم

دستورات کنسول و متغیرها

DatoosTech img Course

قسمت ششم

نوع های داده اولیه

DatoosTech img Course

قسمت هفتم

عملگرهای زبان سی شارپ

DatoosTech img Course

قسمت هشتم

دستورات کنترلی if else

DatoosTech img Course

قسمت نهم

دستورات کنترلی switch

DatoosTech img Course

قسمت دهم

حلقه for

DatoosTech img Course

قسمت دوازدهم

حلقه while

DatoosTech img Course

قسمت دوازدهم

آرایه ها و دستور foreach

DatoosTech img Course

قسمت سیزدهم

متدها در زبان سی شارپ (بخش اول)

DatoosTech img Course

قسمت چهاردهم

متدها در زبان سی شارپ (بخش دوم)

DatoosTech img Course

قسمت پانزدهم

مقدمه برنامه نویسی شی گرا

DatoosTech img Course

قسمت شانزدهم

کلاس، اشیاء و فضاهای نام

DatoosTech img Course

قسمت هفدهم

فیلدها و رفتارها

DatoosTech img Course

قسمت هجدهم

Property ها

DatoosTech img Course

قسمت نوزدهم

سازنده ها و روش های ایجاد اشیاء

DatoosTech img Course

قسمت بیستم

وراثت یا Inheritance

DatoosTech img Course

قسمت بیست و یکم

آشنایی با مفهوم Polymorphism

DatoosTech img Course

قسمت بیست و دوم

کلاس های abstract و sealed

DatoosTech img Course

قسمت بیست و سوم

سازنده ها در وراثت، کلمه کلیدی protected و فیلدهای readonly

DatoosTech img Course

قسمت بیست و چهارم

کلاس های static و partial و Extension Method ها

DatoosTech img Course

قسمت بیست و پنجم

Reference Types و Value Types

DatoosTech img Course

قسمت بیست و ششم

آشنایی با کاربرد interface ها

DatoosTech img Course

قسمت بیست و هفتم

مفاهیم Inversion of Control و Dependency Injection

DatoosTech img Course

قسمت بیست و هشتم

Type Casting و انواع Cast ها

DatoosTech img Course

قسمت بیست و نهم

operator overloading و تعریف cast ها

DatoosTech img Course

قسمت سی ام

مفاهیم boxing و unboxing

DatoosTech img Course

قسمت سی و یکم

قابلیت Generics

DatoosTech img Course

قسمت سی و دوم

نکات تکمیلی Generic ها، کلاس List و کلاس Dictionary

DatoosTech img Course

قسمت سی و سوم

استثناء ها (Exceptions) و مدیریت خطاها

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

همانطور که در قسمت مقدمه گفتیم، Polymorphism به معنای قابلیت تعریف مجدد رفتار یک موجودیت در کلاس های فرزند می باشد. Polymorphism در زبان سی شارپ به سه روش قابل پیاده سازی است:

  1. استفاده از متد های virtual و override کردن آنها در کلاس های فرزند
  2. استفاده از رفتارهای abstract در کلاس والد
  3. استفاده از قابلیت interface ها

در این قسمت، حالت اول را بررسی می کنیم و حالت دوم و سوم، یعنی استفاده از متدهای abstract و interface ها را در بخش های بعدی توضیح خواهیم داد.


متدهای virtual


همانطور که گفتیم یکی از روش های پیاده سازی Polymorphism استفاده از متدهای virtual و override کردن آنها در کلاس فرزند است. برای مثال، فرض کنیم کلاس پایه ای داریم با عنوان Shape که در آن رفتاری با نام Draw تعریف کردیم. رفتار Draw وظیفه ترسیم شئ را بر عهده دارد. در این مثال ها، تنها در متدها پیامی را در پنجره کنسول چاپ می کنیم، اما در محیط واقعی هر یک از این متدها وظیفه ترسیم شئ را بر عهده خواهند داشت. همانطور که گفتیم کلاس Shape رفتار Draw را تعریف می کند. این رفتار در بین تمامی اشیاء ای که از کلاس Shape مشتق می شوند مشترک است. در ابتدا کلاس Shape را به صورت زیر تعریف می کنیم:

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

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

 
            
                public class Rectangle : Shape
                {

                }

                public class Triangle : Shape
                {

                }

                public class Circle : Shape
                {

                }
            
        

هر سه کلاسی که در بالا تعریف کردیم، شامل متد Draw هستند، زیرا این متد در کلاس پایه یعنی 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!
            
        

اما خروجی مدنظر ما تولید نشده است. ما می خواهیم هر کلاس رفتار مربوط به خود را داشته باشد. درست است که رفتار Draw در کلاس پایه تعریف شده، اما باید بتوانیم این رفتار را برای کلاس های فرزند تغییر دهیم. برای اینکار باید در کلاس پایه مشخص کنیم که کدم رفتار را می خواهیم تغییر دهیم. برای اینکار، کافیست رفتار مورد نظر را از نوع virtual تعریف کنیم. اعضای virtual به ما این اجازه را می دهند تا در کلاس فرزند مجدد آنها را تعریف کنیم. برای اینکار متد Draw در کلاس Shape را به صورت زیر تغییر می دهیم:

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

دقت کنید، کلمه کلیدی virtual قبل از نوع بازگشتی متد نوشته می شود. حالا باید در کلاس فرزند رفتار Draw را مجدداً تعریف کنیم. برای اینکار باید متدی که از نوع virtual تعریف شده است را override کنیم. ابتدا رفتار Draw را برای کلاس Rectangle تغییر می دهیم. کد کلاس Rectangle را به صورت زیر تغییر دهید:

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

حالا با اجرای مجدد کد خروجی به صورت زیر تغییر می کند:

 
            
                Drawing rectangle!
                Drawing the shape!
                Drawing the shape!
            
        

زمانی که شما داخل کلاس Rectangle شروع به تایپ می کنید، بعد از نوشتن کلمه کلیدی override و زدن کلید space لیستی از متدهایی که قابل override شدن هستند برای شما نمایش داده می شوند:

Polymorphism1


همچنین در صورتی که ابزار Resharper را نصب کرده باشید، وارد scope کلاس شده و کلید های Alt+Insert را فشار دهید، با اینکار منوی Generate برای شما نمایش داده می شود، از طریق این منو و انتخاب گزینه Overriding members لیستی از تمامی اعضای قابل override شدن به شما نمایش داده می شود:

Polymorphism2


Polymorphism3


بعد از انتخاب عضو مورد نظر و فشار دادن کلید Finish متد مورد نظر برای شما override می شود. مهمترین کاربرد این ویژگی، زمانی است که شما تصمیم دارید چندین ویژگی را با هم override کنید. به یک نکته توجه داشته باشید، چه با روش اول متد را override کنید، چه با روش دوم، کدی برای شما به صورت خودکار درج می شود که به صورت زیر است:

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

در قسمت قبل با کلمه کلیدی base آشنا شدید، در کد بالا که به صورت پیش فرض نوشته می شود، با فراخوانی متد Draw از شئ ای که متد داخل آن override شده، نسخه کلاس پایه از رفتار فراخوانی می شود که شما باید بر اساس نیاز خود کد مورد نظر را برای رفتار override شده بنویسید.

کلاس های Triangle و Circle را نیز به صورت زیر تغییر می دهیم:

 
            
                public class Triangle : Shape
                {
                public override void Draw()
                {
                Console.WriteLine("Draw triangle!");
                }
                }

                public class Circle : Shape
                {
                public override void Draw()
                {
                Console.WriteLine("Draw circle!");
                }
                }
            
        

بعد از اجرای برنامه، خروجی باید به صورت زیر باشد:

Drawing rectangle!
Drawing triangle!
Drawing circle!

شاید خیلی از دوستان این سوال برایشان پیش بیاید که دلیل اینکار چیست؟ ما که متدها را در هر کلاس نوشتیم، چرا باید از کلاس پایه و override کردن آنها استفاده می کردیم؟ مهمترین خاصیت استفاده از Polymorphism استفاده از کلاس پدر برای کارهاست. برای درک بهتر، فرض کنید میخواهیم آرایه ای از اشیاء داشته باشیم. همانطور که می دانید، ما سه کلاس مختلف داریم و در صورت استفاده نکردن از قابلیت وراثت باید برای هر یک از کلاس ها یک آرایه تعریف کنیم. اما با قابلیت وراثت می توان یک آرایه از نوع کلاس پایه تعریف کرد و اشیاء فرزند را داخل آن قرار داد:

 
            
                Shape[] shapes = new Shape[5];
                shapes[0] = new Circle();
                shapes[1] = new Triangle();
                shapes[2] = new Circle();
                shapes[3] = new Rectangle();
                shapes[4] = new Triangle();

                foreach (var shape in shapes)
                shape.Draw();

                Console.ReadKey();
            
        

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

 
            
                Drawing circle!
                Drawing triangle!
                Drawing circle!
                Drawing rectangle!
                Drawing triangle!
            
        

با اینکه ما کلاس پایه یعنی Shape را به عنوان نوع آرایه در نظر گرفتیم، اما در هر خانه از آرایه ای شئ ای از نوع فرزندان کلاس Shape قرار دادیم و به دلیل override کردن رفتار Draw در کلاس های مشتق شده، فراخوانی متد Draw بر اساس تعریفی که در کلاس های فرزند داشتیم انجام می شود.

تعریف مجدد یا override کردن تنها محدود به رفتارها یا همان متدها نمی باشد. شما خصوصیات یا Property ها را نیز می توانید از نوع virtual تعریف کنید. برای مثال کلاسی را با نام Human فرض کنید که دارای سه خصوصیت به نام های FirstName و LastName و FullSpecification می باشد:

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

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

در کلاس بالا، خاصیت FullSpecification نام کامل را بر میگرداند. حالا کلاس فرزندی تعریف می کنیم با نام Employee یا کارمند که از کلاس Human مشتق شده است و خاصیت جدید با نام JobPosition یا موقعیت شغلی به آن اضافه می کنیم:

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

در صورتی که شئ ای از روی Employee بسازیم و خاصیت FullSpecification آن را چاپ کنیم، نام و نام خانوداگی او در خروجی چاپ می شود. اما می خواهیم این خاصیت علاوه بر نام کامل، موقعیت شغلی او را نیز در خروجی چاپ کند. برای اینکار کافیست که در کلاس Human خاصیت FullSpecification را به صورت virtual تعریف کرده و در کلاس فرزند مجدد بخش get آن را تعریف کنیم:

 
            
                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; }
                }
                }
            
        

در کلاس Employee به بخش get توجه کنید:

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

در این قسمت، از کلمه کلیدی base برای گرفتن مقدار FullSpecification استفاده شده است. این خاصیت نام کامل را برای ما بر میگرداند و ما به انتهای آن موقعیت شغلی شخص را اضافه می کنیم و بر میگردانیم.

قابلیت Polymorphism در برنامه نویسی شئ گرا نقش بسیار پر رنگی دارد و خیلی جاها از نوشتن کدهای تکراری جلوگیری کرده و حجم کد برنامه شما را به صورت محسوسی کاهش می دهد. در بخش بعدی آموزش زبان سی شارپ، با کلاس ها و متدهای abstract و همچنین کلاس های sealed آشنا می شویم.