PDA

View Full Version : آموزش: آموزش کامل شی گرایی در سی شارپ



میلاد رئیسی
پنج شنبه 30 خرداد 1392, 20:03 عصر
مطالبي كه در اين پست خواهيد ديد :
شيء چيست؟
ارث‌بري يا Inheritance
انتزاع يا Abstraction
كپسوله‌كردن يا Encapsulation
چندريختي يا Polymorphism
يکی از مهمترين و اصلی ترين مشکلات برای افرادی که به تازگی با محيط‌های برنامه نويسی شی‌گرا آشنا می شوند، درک مفاهيم شيءگرائي است. در حقيقت درک مفاهيمي چون شیء و مولفه (Component) بسيار دشوار نيست، کافيست کمي به اطراف خود با دقت نگاه کنيم. ما در دنيايي از اشياء مختلف زندگی ميکنيم. تلويزيون، راديو در و پنجره، همه و همه نمونه هايی از اشياء مختلفي هستند که در اطراف ما وجود دارند. اما درک و پياده‌سازی اين مفهوم در يک زبان برنامه‌سازی اندکی متفاوت است.

شیء چيست؟
همانطور که گفتيم، با يک ديد تصويری به سادگی می توانيد اشياء مختلفی را در اطراف خود بيابيد. تمامی اين اشياء دارای سطوح و درجه پيچيدگی متفاوتی هستند. پيچيدگی آنها به شکل ظاهری و نوع رفتار آنها بستگی دارد.
در شیء گرايی به "شکل ظاهر" در اصطلاح، صفت يا Attribute و به عملی که شیء انجام می دهد، رفتار يا Behavior می گويند.
برای مثال يک صندلی را در نظر بگيريد. صندلی صفات مختلفی دارد ولی رفتار خاصی ندارد. مثلاً پايه های صندلی جزو صفات آن بشمار می روند. با کمی دقيق تر شدن می توان از اين صفات برای توصيف صندلی استفاده کرد. بعنوان مثال تعداد پايه های صندلی می تواند عددی بين 3 تا 5 باشد. محل نشستن صندلی می تواند جمله‌اي در وصف جنس آن و مقدار مصرف ماده سازنده آن باشد. پشتی صندلی را نيز می توان بعنوان متغييری boolean در نظر گرفت چراکه برخی از صندلی ها فاقد پشتی هستند. با استفاده از اين سه صفت ساده، به راحتی می توان صندلی را توصيف نمود و با همين سه صفت ميتوان گونه‌های مختلفی از صندلی را نيز توصيف کرد.

منظور از رفتار، عملی است که يک شیء انجام می دهد. از اينرو برای صندلی نمی توان به سادگی صفات آن، رفتاری را متصور شد. مثلاً مي‌توانيم بگوئيم تاشو بودن صندلی يکی از رفتارهای آن می تواند باشد، چراکه عملی است كه می تواند يک صندلی آنرا انجام دهد.

حال شیء ديگری مانند تلويزيون را در نظر بگيريد. صفاتی که می توان برای تلويزيون در نظر گرفت عبارتند از : صفحه نمايش، سازنده آن و ... برای تلويزيون به راحتی می توان رفتار در نظر گرفت : خاموش و روشن شدن، تغيير کانال و کم و زياد کردن صدا. اين رفتارها بر اثر درخواست يک انسان يا همان کاربر اتفاق می افتند.

بطور کلی، يک شیء را ميتوان بوسيله صفات و رفتارهای آن بطور کامل توصيف نمود. يک شیء حتماً جسمی فيزيکی نيست، بلکه هر چيز قابل تصوری است که دارای صفت و رفتار است. در حقيقت ميتوان گفت يک شیء شبيه به يک اسم است. اگر بتوان برای اين اسم، صفت و رفتاری تعريف کرد، آن وقت تبديل به شیء می شود.

از ديد انتزاعی، زمان را نيز می توان بعنوان يک شیء در نظر گرفت. صفات زمان، ساعت، دقيقه و ثانيه هستند و گذشت زمان، رفتار آن است. در ايجاد شیء هيچ محدوديتی وجود ندارد و همه چيز به تخيل شما باز ميگردد.

در زبان C#، اشياء بوسيله کلاسها (Class) نمايش داده می شوند. داخل کلاس، صفات بصورت فيلدها ظاهر می شوند و جهت پياده سازی رفتارها از متدها استفاده می گردد. به مثال زير توجه نمايد :

class Time
{
int hours;
int minutes;
int seconds;

void pastime()
{
//some implementation
}
}


در اين مثال، کلاس Time مشاهده می شود. اين کلاس با کلمه کليدی class اعلان گرديده است. همانطور که ميدانيد، دو کروشه باز و بسته {} نيز ابتدا و انتهای کلاس را مشخص مي‌کند. فيلدها دارای نام و نوع هستند. متدها دارای نام و نوع بازگشتی می باشند و پياده‌سازی آنها داخل بلوک مربوط به خود آنها (بين دو {}) انجام ميگردد.

يک شیء مي‌تواند هر يک از نيازمندي‌های يک پروژه باشد. طراحی و اعلان صحيح اشياء و مشخص كردن صفات و رفتار آنها يکی از مقوله‌های مهم در مهندسی نرم افزار بر پايه شیء‌گرائي است، چراکه همگی تراکنشها بين اشياء صورت می پذيرند.

کلاسه کردن اشياء (مقدمه ای بر ارث بری Inheritance)
طبقه‌بندی اشياء در گروههای مختلف بسيار سودمند است. زمين شناسان سنگها را طبقه بندی مي‌کنند و زيست شناسان گياهان و حيوانات را طبقه‌بندی مي‌کنند. طبقه بندی اشياء باعث مي‌شود تا با دقت و ظرافت بيشتری بتوان به جزئيات هر طبقه و يا هر نوع پرداخت.

برای مثال حيوانات را در نظر بگيريد. زيست شناسان حيوانات را به دسته‌های پرندگان، مهره داران و خزندگان تقسيم مي‌کنند. پرندگان صفاتی همچون منقار و بال دارند، مهره‌داران بدنی مودار دارند و خون گرمند و خزندگان خون سردند. از ديد رفتاری نيز پرندگان پرواز می کنند، مهره داران به بچه های خود شير مي‌دهند و خزندگان مي‌خزند. اينها ويژگيهای ارشد يا عام هستند و صفات ديگری در زير گروهها افزوده مي‌شوند. در گروه اصلی تنها صفات و رفتارهايی قرار مي‌گيرند که عموميت داشته باشند و ساير صفات و رفتارها در زير گروهها معين مي‌گردند. بعنوان مثال کلاس پرندگان را بشکل زير ميتوان پياده‌سازی نمود :

class Bird
{
string beakDescription;
int wingSpan;
string typeOfBird;

void fly()
{
//some implementation
}
}

می توان اين کلاس را بعنوان کلاسی عمومی برای پرندگان در نظر گرفت که دارای فيلدی جهت تعيين نوع پرنده نيز مي‌باشد. با استفاده از فيلد typeOfBird مي‌توان گونه پرنده مورد نظر را معين نمود.

سلسله مراتب اشياء (بررسی ارث بری در محيط شیءگرا)
روش گفته شده در بالا، جهت افزودن صفات برای يک طبقه‌بندی ساده کارآمد است، اما برای طبقه‌بندي‌های پيچيده قابل قبول نيست. تصور کنيد حجم عظيمی نيازمندی در پروژه وجود داشته باشد و به سطح های مختلفی از طبقه بندی نياز داشته باشيد. با افزودن صفات جديد به هر يک از طبقات و سطحها به پيچيدگی پروژه به شدت افزوده می شود.

اتفاقی که در مورد زير شاخه‌ها رخ مي‌دهد، ايجاد يک سلسله مراتب طبيعی است. برنامه‌نويسی شیءگرا ، متدولوژيی را جهت مديريت سلسله مراتب طبيعی فراهم می نمايد. بعنوان مثال، اگر در يک پروژه يکی از طبقه بنديهای ما حيوانات باشند، منطقی است که شیءاي از طبقه حيوانات در رأس سلسله مراتب قرار گيرد و در طبقات و زير شاخه‌های سطوح پائينی، پرندگان، مهره داران و خزندگان قرار گيرند. در سطح بعدی مي‌توان چند پرنده مانند اردک، کلاغ و ... را نيز در زير شاخه پرندگان قرار داد. اين سلسله مراتب تا سطح مورد نظر قابل تعميم است.

در برنامه نويسی شیءگرا، مفهومی که اشياء را تحت يک سلسله مراتب خاص قرار ميدهد، ارث‌بری يا Inheritance ناميده مي‌شود. مثلاً طبقه‌بندی حيوانات را در نظر بگيريد، شیءای که در بالاترين سطح قرار مي‌گيرد، شیء Animal است. اين شیء دارای ويژگيهايی بسيار کلی و عمومی است و بايد چنين هم باشد، چراکه سرگروه بايد هميشه ويژگيهايی را داشته باشد که در زير شاخه ها بطور مشترک وجود دارد و هر يک از زير شاخه‌ها به ويژگيهای سرگروه خود، ويژگيها و مشخصات خاص خود را می افزايند.
در اين مثال، سرگروه Animal تنها مي‌تواند دارای صفت يگانه "زيستن" باشد، چراکه همين صفت آنرا از ساير اشياء، نظير سنگ و در و پنجره و بطور کل اجسام، متمايز می‌نمايد. رفتار اين شیء نيز می تواند "تنفس کردن" و "غذا خوردن" باشد. حال برای اينکه شیءای بتواند در اين سلسله مراتب وارد شود، حداقل بايد دارای صفت شیء سرگروه اين سلسله مراتب باشد، درغير اينصورت نمي‌تواند در زير شاخه آن قرار گيرد.

تفاوت Animal با ساير اشياء که در اين سلسله مراتب قرار مي‌گيرند در آنست که ساير اشياء مي‌بايست صفاتی ديگر و - يا رفتارهای ديگری را نيز به صفات و رفتارهای Animal اضافه کنند. اين صفات و رفتارها مسلماً جزئی‌تر و دقيق‌تر از صفات و رفتارهای سرگروه است. همين مسئله مفهومی را در پيمايش سلسله مراتب‌ها بوجود می‌آورد : در پيمايش بالا به پائين (Top-Down) سلسله مراتب‌ها به جزئيات يا گونه‌های خاص برخورد مي‌کنيم، حال آنکه در پيمايش پائين با بالا (Bottom-Up) به گروهها و دسته‌های عمومی مي‌رسيم.

اشياء سطوح پائينی (که به آنها child مي‌گوئيم) صفات و رفتارهای اشياء سطح بالاتر خود را به ارث مي‌برند. به اين اشياء بالاتر يا سرگروهها نيز parent مي‌گوئيم. به اين رابطه موجود بين child و parent در اصطلاح رابطه "هست" يا "بودن" (is-a relationship) مي‌گويند. مثلاً مي‌گوئيم "اردک يک پرنده است".

سادگی ارث بری از نحوه ايجاد سلسله مراتب ارث‌بری نشأط مي‌گيرد. اشياء سطوح پائينی (child) در تعريف خود اشياء سطوح بالايی (parent) خود را مشخص مي‌کنند. در اين جا تنها کاری که لازم است يک child نسبت به parent خود انجام دهد افزودن صفات و رفتارهای مربوط به خود است.

علاوه بر طبقه‌بندی اشیاء در دسته‌های مختلف و سادگی در سازماندهی آنها بوسيله ارث‌بری، استفاده از ارث‌بری در انجام کارها نيز صرفه‌جويی ايجاد مي‌کند. هر شیء جديدی که به يک سلسله مراتب وارد مي‌شود، بطور خودکار تمامی صفات و رفتارهای کليه parent های خود را دارا مي‌باشد و بعلت ارث‌بری نيازی به تعريف مجدد اين صفات برای شیء جديد نمی‌باشد. به بيان ديگر مي‌توان گفت، ارث‌بری روشی برای استفاده مجدد از صفات و رفتارهای موجود است.

استفاده از ارث‌بری اين امکان را برای طراحان نرم افزار فراهم مي‌کند تا وقت بيشتری برای تفکر بر روی منطق برنامه صرف کنند و درگير پيچيدگي‌های پياده‌سازی و نگهداری نرم افزار نشوند.

انتزاع (Abstraction)
اکنون زمان مناسبی برای بحث درباره انتزاع است. برخی اشياء تا حدودی انتزاعی هستند و برخی ديگر کاملاً واقعی. بعنوان مثال، چيزی بعنوان Animal وجود ندارد، بلکه اين تنها توصيف کلاسی از اشياء است. همچنين موجوديتی وجود ندارد که از لحاظ فيزيکی يک پرنده باشد. اين تنها طبقه‌بندی و دسته‌بندی است که مورد استفاده قرار مي‌گيرد.

از طرف ديگر شیءای وجود دارد بنام اردک که واقعاً يک اردک است و دارای کليه صفات و رفتارهای آن مي‌باشد. البته بايد توجه داشت که هر اردک تنها نمونه‌ای از "اردک" است. (منظور در اينجا آنست که موجوديتی مانند يک اردک که واقعا وجود دارد، خود نمونه‌ای (instance) از کلاس اردک است. درک مطالب در اينجا شايد به اندکی تأمل و حوصله نياز داشته باشد!)

برای شیءای مانند Animal يا Bird، نمی‌توان صفتی همچون "پاهايی پهن" و يا رفتاری مانند "را رفتن شبيه به اردک" را تعريف نمود. همچنين از اشياء Animal و Bird تنها يک نمونه مي‌تواند وجود داشته باشد که اين نمونه‌ها نيز بسيار مهم هستند، چراکه اين نمونه‌ها هستند که ساختار يک سلسله مراتب را تشکيل مي‌دهند و صفات و رفتارهای کلی آن را معين مي‌نمايند. ( البته توجه کنيد که استفاده از کلمه abstract در زبان C# خود يگانه بودن و همچنين انتزاعی بودن کلاس را نشان مي‌دهد.)

تعريف و توصيف صحيح اين اشياء انتزاعی، همچون Animal و Bird، در طبقه‌بندی و کارآمدی ساختار سلسله مراتبی بسيار می‌تواند موثر باشد. مثال زير نحوه تعريف و ايجاد اشياء انتزاعی در C# را نشان می دهد.

abstract class Animal
{
//abstract definitions and implementations
}

class Bird : Animal
{
//class implementation
}


در اين مثال کلاس Animal بصورت abstract اعلان شده تا مشخص شود که شیءای انتزاعی است. چون شیء انتزاعی عملاً وجود ندارد، نمی‌توان نمونه‌ای جديد از روی آن ايجاد کرد و تنها يک شیء از آن وجود دارد. در کلاس دوم، Bird، نشان داده شده است که اين کلاس از کلاس انتزاعی Animal ارث‌بری دارد. اين عمل با استفاده از ":" در جلوی نام Bird و سپس به دنبال آن نام کلاسی که Bird از آن ارث‌بری مي‌کند، يعنی همان Animal، صورت گرفته است.

اشياء درون اشياء (مقدمه ای بر کپسوله‌کردن يا Encapsulation)
ساختارهای سلسله مراتبی روشی جهت دستيابی به روابط بين اشياء هستند. هر چند روشهای ديگری نيز برای نشان دادن روابط بين اشياء وجود دارد. يکی از اين روشها که بسيار معمول نيز هست، استفاده از اشياء درون اشيائی ديگر است.

يکی از اصطلاحات رايج برای بيان اين مفهوم، کپسوله‌کردن يا Encapsulation است. استفاده صحيح از اين مفهوم باعث کاهش پيچيدگی شده و تنها آن قسمت اطلاعات و جزئيات را نمايان مي‌کند که برای درک يک شیء لازم است. بعنوان مثال، پرواز پرندگان مسئله‌ای است که توجه بسياری از زيست شناسان را به خود جلب کرده است. آنها برای درک پرواز، ساختار بالها، پرها و اسکلت پرندگان را مورد مطالعه قرار می‌دهند. برای درک پرواز، بررسی همين اجزاء و جزئيات کافيست و نيازی به مطالعه درباره سيستم گوارش پرندگان نمی‌باشد. برای بررسی يك موضوع تنها اطلاعاتی مربوط به آن در حل و بررسی مسئله می‌تواند موثر باشد و اطلاعات اضافی، نه تنها وقت‌گير و بی فايده است، بلکه می‌تواند گيج کننده نيز باشد و بر پيچيدگی مسئله ، بدون دليل، بيفزايد. با استفاده از کپسوله‌کردن تنها آن قسمت از اطلاعات که مورد نظر است در دسترس قرار مي‌گيرند.

شیء Bird را که قبلاً درباره آن صحبت کرديم، در نظر بگيريد. پرندگان دارای منقار و بال هستند. خود بال يک شیء است که دارای صفاتی نظير پر و اندازه است. رفتار آن نيز می‌توان باز و بسته شدن در حين پرواز باشد. نکته قابل اشاره در اينجا، کلمه "داشتن" است. اعمالی وجود دارند که پرنده آنها را انجام مي‌دهد و اين اعمال خود جزئی از پرنده هستند.

بطور کلی، رابطه "داشتن" (has-a relationship) بين يک شیء و اعمالی که بر روی اجزای خود انجام مي‌دهد، وجود دارد. بعنوان مثال مي‌گوئيم "پرنده بال دارد". در اينجا چون بال شیءای متعلق به پرنده است که پرنده روی آن عملی انجام مي‌دهد، مفهوم کپسوله‌کردن رخ مي‌دهد، شیءای درون شیءای ديگر. مثال زير نحوه پياده‌سازی کپسوله‌کردن را نشان مي‌دهد.

class Wing
{
int foreWingSize;
int backWingSize;

void flap()
{
//implementation
}

void fold()
{
//implementation
}
}

class Bird : Animal
{
int beakSize;
Wing wings;

void Fly()
{
//implementation
}
}


در اين مثال، دو کلاس Bird و Wing وجود دارند. کلاس Wing فيلدها و متدهای مربوط به خود را دارد. درون کلاس Bird اعلانی از کلاس Wing با استفاده از نام wings صورت گرفته است. اين عمل رابطه مالکيتی بين Bird و Wing ايجاد مي‌نمايد. "پرنده دارای بال است". تنها چيزی که کافيست بدانيم آنست که کلاس انتزاعی پرنده دارای بال است. توجه کنيد که صفات و رفتارهای کلاس Wing بوسيله خود اين کلاس کنترل مي‌شوند.

اشيائی با رفتارهايی متفاوت (مقدمه ای بر چندريختی يا Polymorphism)
در برخی شرايط ممکن است نياز داشته باشيم تا علاوه بر اينکه شیءای را در رده خاصی طبقه‌بندی می‌کنيم، به آن شیء اين اجازه را نيز بدهيم تا بتواند رفتارهای خاص به خود را داشته باشد.

برای مثال، اردک و يا عقاب اشيايی هستند که با گروه Bird ارتباط دارند. تصور کنيد زيست‌شناسی مي‌خواهد رفتارهای اين پرندگان را بررسی نمايد. برای بررسی اين پرندگان لازم است تا زيست شناس آنها را در جايی نگهداری کرده و بر روی آنها مطالعه انجام دهد. مسلماً اين پرندگان بايد در قفس نگهداری شوند اما مطمئناً قفس اردک با قفس عقاب يکسان نمی‌تواند باشد. اين بدين معناست، قفسی را که تعريف مي‌کنيم بايد طوری باشد تا برای اکثر گونه‌های پرندگان مناسب باشد. مي‌توانيم قفسی مخصوص پرندگانی که پرواز مي‌کنند در نظر بگيريم. از اينرو پرندگانی را که پرواز مي‌کنند را در اين قفس قرار مي‌دهيم. همانطور که مشاهده مي‌کنيد، دسته‌بندی پرندگان در يک گروه جهت رسيدن به اهداف پروژه ضروری است. پياده‌سازی مطلب گفته شده بشکل زير است .

abstract class flyingBird : Bird
{
//implementation
}

class Eagle : flyingBird
{
//implementation
}

class Duck : flyingBird
{
//implementation
}

class Experiment
{
public static void Main()
{
flyingBird flyingBirdCage = new flyingBird[2];
flyingBirdCage[0] = new Eagle();
flyingBirdCage[1] = new Duck();
}
}


در کد فوق، 3 کلاس جديد ايجاد شده است. اولين کلاس، flyingBird است که از کلاس Bird مشتق شده است. از آنجائيکه تمامی پرندگان پرواز نمی‌کنند، اين کلاس مي‌تواند تنها مخصوص آن دسته از پرندگانی باشد که مي‌توانند پرواز نمايند. کلاسهای Eagle و Duck در اين کد از FlyingBird مشتق شده‌اند. آخرين کلاس نيز Experiment است که متد Main() در آن قرار گرفته است. درون متد Main()، آرايه‌ای بنام flyingBirdCage از نوع FlyingBird تعريف شده که اين آرايه، مخصوص اشيايی از نوع FlyingBird است. از آنجائيکه کلاسهای Duck و Eagle از نوع FlyingBird هستند، پس مي‌توان آنها را در اين آرايه قرار داد. نکته مهم در اينجا نيز همين مطلب است که از اين پس ما پرندگانی داريم که پرواز مي‌کنند و در قفس مخصوص نگهداری می‌شوند. حال تصور کنيد که اين نوع قفس وجود نداشت و مجبور بوديم تا برای هر پرنده نوع خاصی از قفس را تعريف نمائيم. در اين شرايط حجم کار بسيار زياد مي‌شد و به مشکل بر می‌خورديم. اما هم اکنون می‌دانيم که قفسی داريم که شرايط کلی و عمومی برای نگهداری پرندگانی که پرواز می‌کنند را داراست. در صورتيکه برای هر پرنده بخواهيد قفسی مخصوص ايجاد کنيد، کدی شبيه به کد زير مورد نياز بود :

class Experiment
{
public static void Main()
{
Eagle eagleCage = new Eagle();
Duck duckCahge = new Duck();
}
}


علاوه بر اين تصور کنيد که بخواهيم مشخصات ديگری نيز به کلاسها بيفزائيم. در اين شرايط کار با کدام گزينه ساده‌تر است؟ کار با آرايه‌ای از اشياء يکسان، يا کار با اشيايی متفاوت؟ مسلماً کار با آرايه‌ها ساده‌تر است چرا که با استفاده از تنها يک دستور foreach مي‌توان کليه عناصر يک آرايه را پيمايش کرد.

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

عمل پرواز کردن، در کليه اين پرندگان وجود دارد، ولی نحوه پرواز در هر يک از آنها مسلماً متفاوت است. اين مفهوم دقيقاً با مفهوم چندريختی (Polymorphism) در شیءگرايی مطابقت دارد. Polymorphism توانايی انجام يک عمل توسط اشياء مختلف است که به شکلهای مختلف انجام می گيرد. مثال زير نحوه پياده‌سازی Polymorphism در زبان C# را نشان می‌دهد.

using System;

class FlyingBird
{
public virtual void Fly()
{
Console.WriteLine("This shouldn't be called");
}
}

class Eagle : FlyingBird
{
public override void Fly()
{
Console.WriteLine("Eagle Fly");
}
}

class Duck : FlyingBird
{
public override void Fly()
{
Console.WriteLine("Duck Fly");
}
}

class Experiment
{
public static void Main()
{
FlyingBird[] flyingBirdCage = new FlyingBird[2];

flyingBirdCage[0] = new Eagle();
flyingBirdCage[1] = new Duck();

foreach(FlyingBird bird in flyingBirdCage)
{
bird.Fly();
}
}
}

خروجی اين برنامه بشکل زير است :

Eagel Fly
Duck Fly


در اين مثال، کلاسهای Eagel و Duck از کلاس FlyingBird مشتق شده‌اند. هر يک از اين کلاسها دارای متد Fly() هستند. تنها تفاوت اين متدها در نحوه اعلان و پياده‌سازی آنهاست. در کلاس FlyingBird، متد Fly() بصورت virtual اعلان شده است، بدين معنا که کلاسهای مشتق شده از اين کلاس می‌توانند شخصاً به پياده‌سازی اين متد بپردازند. پياده‌سازی اين متد در کلاسهای مشتق شده با استفاده از کلمه کليد override صورت می‌گيرد. در متد Main() با استفاده از يک حلقه foreach، تک تک اين متدها فراخوانی می‌شوند. بعلت اينکه کليه اين متدها بصورت override تعريف شده‌اند، در فراخوانی بصورت مجزا اجرا می‌شوند.

به همين ويژگی، يعنی داشتن يک رفتار در گروهی از اشياء که هر يک از آنها اين رفتار را بصورت دلخواه خود پياده‌سازی می‌کنند، Polymorphism گفته مي‌شود. استفاده از Polymorphism در حل مسايل پيچيده بسيار سودمند است.

میلاد رئیسی
پنج شنبه 30 خرداد 1392, 20:07 عصر
Property ها امكان ايجاد حفاظت از فيلدهاي يك كلاس را از طريق خواندن و نوشتن بوسيله Property را فراهم مي‌نمايد. Property ها علاوه بر اينكه از فيلدهاي يك كلاس حفاظت مي‌كنند، همانند يك فيلد قابل دسترسي هستند. بمنظور درك ارزش Property ها بهتر است ابتدا به روش كلاسيك كپسوله كردن متدها توجه نماييد.

مثال 1: يك نمونه از چگونگي دسترسي به فيلدهاي كلاس به طريقه كلاسيك

using System;
public class PropertyHolder
{
private int someProperty = 0;
public int getSomeProperty()
{
return someProperty;
}
public void setSomeProperty(int propValue)
{
someProperty = propValue;
}
}
public class PropertyTester
{
public static int Main(string[] args)
{
PropertyHolder propHold = new PropertyHolder();
propHold.setSomeProperty(5);
Console.WriteLine("Property Value: {0}", propHold.getSomeProperty());
return 0;
}
}
مثال 1 روش كلاسيك دسترسي به فيلدهاي يك كلاس را نشان مي‌دهد. كلاس PropertyHolder داراي فيلدي است تمايل داريم به آن دسترسي داشته باشيم. اين كلاس داراي دو متد getSomeProperty() و setSomePropery() مي‌باشد. متد getSomeProperty() مقدار فيلد someProperty را باز مي‌گرداند و متد setSomeProperty() مقداري را به فيلد someProperty تخصيص مي‌دهد.

كلاس PropertyTester از متدهاي كلاس PropertyHolder جهت دريافت مقدار فيلد someProperty از كلاس PropertyHolder استفاده مي‌كند. در متد Main() نمونه جديدي از شي PropertyHolder با نام propHold ايجاد مي‌گردد. سپس بوسيله متد setSomeProperty، مقدار someMethod از propHold برابر با 5 مي‌گردد و سپس برنامه مقدار property را با استفاده از فراخواني متد Console.WriteLine() در خروجي نمايش مي‌دهد. آرگومان مورد استفاده براي بدست آوردن مقدار property فراخواني به متد getSomeProperty() است كه توسط آن عبارت “Property Value : 5” در خروجي نمايش داده مي‌شود.

چنين متد دسترسي به اطلاعات فيلد بسيار خوب است چرا كه از نظريه كپسوله كردن شيء‌گرايي پشتيباني مي‌كند. اگر پياده‌سازي someProperty نيز تغيير يابد و مثلا از حالت int به byte تغيير يابد، باز هم اين متد كار خواهد كرد. حال همين مسئله با استفاده از خواص Property ها بسيار ساده‌تر پياده‌سازي مي‌گردد. به مثال زير توجه نماييد.

مثال 2: دسترسي به فيلدهاي كلاس به استفاده از Property ها

using System;
public class PropertyHolder
{
private int someProperty = 0;
public int SomeProperty
{
get
{
return someProperty;
}
set
{
someProperty = value;
}
}
}
public class PropertyTester
{
public static int Main(string[] args)
{
PropertyHolder propHold = new PropertyHolder();
propHold.SomeProperty = 5;
Console.WriteLine("Property Value: {0}", propHold.SomeProperty);
return 0;
}
}


مثال 2 چگونگي ايجاد و استفاده از ويژگيها (Property) را نشان مي‌دهد. كلاس PropertyHolder داراي پياده‌سازي از ويژگي SomeProperty است. توجه نماييد كه اوليد حرف از نام ويژگي با حرف بزرگ نوشته شده و اين تنها تفاوت ميان اسم ويژگي SomeProperty و فيلد someProperty مي‌باشد. ويژگي داراي دو accessor با نامهاي set و get است. accessor get مقدار فيلد someProperty را باز مي‌گرداند. set accessor نيز با استفاده از مقدار value، مقداري را به someProperty تخصيص مي‌دهد. كلمه value كه در set accessor آورده شده است جزو كلمات رزرو شده زبان C#‎‎‎‎‎‎‎ مي‌باشد.

كلاس PropertyTester از ويژگي someProperty مربوط به كلاس PropertyHolder استفاده مي‌كند. اولين خط در متد Main() شي‌اي از نوع PropertyHolder با نام propHold ايجاد مي‌نمايد. سپس مقدار فيلد someProperty مربوط به شيء propHold، با استفاده از ويژگي SomeProperty به 5 تغيير مي‌يابد و ملاحظه مي‌نماييد كه مسئله به همين سادگي است و تنها كافي است تا مقدار مورد نظر را به ويژگي تخصيص دهيم.

پس از آن، متد Console.WriteLine() مقدار فيلد someProperty شيء propHold را چاپ مي‌نمايد. اين عمل با استفاده از ويژگي SomeProperty شيء propHold صورت مي‌گيرد.

ويژگيها را مي‌توان طوري ايجاد نمود كه فقط خواندني (Read-Only) باشند. براي اين منظور تنها كافيست تا در ويژگي فقط از get accessor استفاده نماييم. به مثال زير توجه نماييد.

ويژگيهاي فقط خواندني (Read-Only Properties)
مثال 3: ويژگيهاي فقط خواندني

using System;
public class PropertyHolder
{
private int someProperty = 0;
public PropertyHolder(int propVal)
{
someProperty = propVal;
}
public int SomeProperty
{
get
{
return someProperty;
}
}
}
public class PropertyTester
{
public static int Main(string[] args)
{
PropertyHolder propHold = new PropertyHolder(5);
Console.WriteLine("Property Value: {0}", propHold.SomeProperty);
return 0;
}
}


مثال 3 چگونگي ايجاد يك ويژگي فقط خواندني را نشان مي‌دهد. كلاس PropertyHolder داراي ويژگي SomeProperty است كه فقط get accessor را پياده‌سازي مي‌كند. اين كلاس PropertyHolder داراي سازنده‌ايست كه پارامتري از نوع int دريافت مي‌نمايد.

متد Main() از كلاس PropertyTester شيء جديدي از PropertyHolder با نام propHold ايجاد مي‌نمايد. اين نمونه از كلاس PropertyHolder از سازندة آن كه مقداري صحيح را بعنوان پارامتر دريافت مي‌كند، استفاده مي‌كند. در اين مثال اين مقدار برابر با 5 در نظر گرفته مي‌شود. اين امر باعث تخصيص داده شدن عدد 5 به فيلد someProperty از شيء propHold مي‌شود.

تا زمانيكه ويژگي SomeProperty از كلاس PropertyHolder فقط خواندني است، هيچ راهي براي تغيير مقدار فيلد someProperty وجود ندارد. بعنوان مثال در صورتيكه عبارت propHold.SomeProperty = 7 را در كد برنامه اضافه نماييد، برنامة شما كامپايل نخواهد شد چراكه ويژگي SomeProperty فقط خواندني است. اما اگر از اين ويژگي در متد Console.WriteLine() استفاده نماييد بخوبي كار خواهد كرد زيرا اين دستور تنها يك فرآيند خواندن است و با استفاده از get accessor اين عمل قابل اجرا است.

ويژگيهاي فقط نوشتني (Write-Only Properties)
به مثال زير توجه فرماييد :

مثال 4 : ويژگيهاي فقط خواندني

using System;
public class PropertyHolder
{
private int someProperty = 0;
public int SomeProperty
{
set
{
someProperty = value;
Console.WriteLine("someProperty is equal to {0}", someProperty);
}
}
}
public class PropertyTester
{
public static int Main(string[] args)
{
PropertyHolder propHold = new PropertyHolder();
propHold.SomeProperty = 5;
return 0;
}
}


مثال 4 چگونگي ايجاد و استفاده از ويژگي فقط نوشتني را نشان مي‌دهد. در اين حالت get accessor را از ويژگي SomeProperty حذف كرده و به جاي آن set accessor را قرار داده‌ايم.

متد Main() كلاس PropertyTester شي‌اي جديد از همين كلاس با سازندة پيش فرض آن ايجاد مي‌نمايد. سپس با استفاده از ويژگي SomeProperty از شيء propHold، مقدار 5 را به فيلد someProperty مربوط به شيء propHold تخصيص مي‌دهد. در اين حالت set accessor مربوط به ويژگي SomeProperty فراخواني شده و مقدار 5 را به فيلد someProperty تخصيص مي‌دهد و سپس عبارت someProperty is equal to 5” “را در خروجي نمايش مي‌دهد.


دوست عزیز پیشنهاد میکنم همه مباحث شی گرا رو کامل یاد بگیرید ! کتاب زیاد در این زمینه هست !

میلاد رئیسی
پنج شنبه 30 خرداد 1392, 20:10 عصر
مقدمه :
در سیستمهای مهندسی و بخصوص سیستمهای نرم افزاری برای غلیه بر پیچیدگی , کل سیستم رو به چند Sub System تقسیم میکنن . در حین طراحی هر Sub system , ارتباط اون با سایر قسمتهای سیستم هم در نظر گرفته میشه و در نهایت این مجموعه رو به صورت یک مجموعه واحد در نظر می گیریم

معماری چند لایه : n-Tier
به طور کلی در معماری چند لایه , هر لایه T , سرویسهایی رو از لایه قبلی خودش T-1 دریافت و سرویسهایی رو به لایه بعدی خودش یعنی T+1 ارائه میده .

معماری سه لایه :
یکی از انواع معماری های چند لایه معماری سه لایه هست Three Tier ... ایده بوجود اومدن معماری سه لایه بعد از بوجود اومدن Web Page های Dynamic بود .
در این معماری Object های نرم افزار در سه لایه طراحی میشن .
بخش ها یا لایه های اصلی نرم افزار در این معماری عبارتند از :

1 - Presentation Layer : یا همون لایه Interface نرم افزار ... فرمها , واسطها و منوهاییو هر چیزی که برای کاربر قابل رویت باشه , در نرم افزارهای تجاری و کاربردی همگی در لایه نمایش یا Presentation قرار دارن . این لایه در ارتباط با کاربر هست و حاوی عناصر User Interface (رابط گرافیکی کاربر) و شامل تمامی منطق حکم فرما در نحوه ی ارتباط کاربر با اجزای سایت می باشد. لایه نمایش، بخشی از سایت است که کاربران می توانند آنرا مشاهده کنند و با استفاده از عناصر آن، از امکانات سایت استفاده کنند، بنابراین طراحی صحیح و اصولی این بخش تاثیر بسزایی در موفقیت وب سایت دارد.

2 - Business Logic Layer : یا لایه تجاری که در بر گیرنده منطق اصلی برنامه هست .
در این لایه اعمال اصلی نرم افزار با استفاده از همکاری با لایه های پایین و بالا انجام میشه . در این لایه کار های مرتبط با DataBase وجود نداره و این وظایف تماما به لایه Data Access سپرده میشه .
این لایه در ارتباط با تیم برنامه نویسی هست.

این لایه که معمولا لایه میانی (Middle Tier) نیز نامیده می شود وظیفه ارتباط بین لایه نمایش و لایه داده را بر عهده دارد. کلیه Request هایی (درخواست ها) که در اثر تعامل کاربر با لایه نمایش ایجاد می شود (به استثنا مواردی که توسط خود لایه نمایش مدیریت می شود، مانند Validation یا اعتبار سنجی فیلدها) به این لایه ارسال و نتیجه حاصل از پردازش، بر اساس منطق تعیین شده در این لایه، مجددا به لایه نمایش برگردانده و در آنجا به کاربر نمایش داده می شود.
در بیشتر مواقع لایه منطق برای پاسخگویی به درخواست های لایه نمایش باید با لایه داده ها ارتباط برقرار کند. به بیان ساده تر، مثلا زمانی که کاربر عملیات جستجو در محصولات را در سایت انجام می دهد، لایه نمایش به لایه منطق می گوید: "محصولاتی که نام آنها با این کلمات تطابق دارد را برای من ارسال کن". در این هنگام لایه منطق کلمات ارسال شده از لایه نمایش را به لایه داده ها می دهد و نتایج جستجو را از آن تحویل می گیرد و مجددا به لایه نمایش ارسال می کند.

3 - Data Access Layer : لایه دسترسی به داده ها پایین ترین لایه در معماری سه لایه و البته مهمترین لایه در معماری سه لایه .
این لایه در رتباط با تیم مدیریتی و تیم برنامه نویسی هست .
لایه Data وظیفه مدیریت اطلاعات موجود در دیتابیس را بر عهده دارد و بر اساس دستوراتی که لایه Business به آن می دهد، اطلاعاتی را در دیتابیس اضافه، حذف، ویرایش و یا جستجو می کند و نتیجه این اعمال را به Business Tier باز می گرداند.

مزایای معماری سه لایه :
مهمترین دستاورد معماری سه لایه : استقلال قسمتهای مختلف پروژه با همدیگست .
تغییر زمینه کاربرد در حداقل زمان ... مثلا برنامه از Win App به Web App تبدیل بشه یا DataBase برنامه رو میشه به راحتی تغییر داد .

معایب معماری سه لایه :
از معماری سه لایه برای پروژه های کوچیک نمیشه استغاده کرد به علت حجم زیاد فایلها در این معماری .
Performance برنامه در معماری سه لایه پایینه چون ارتباطات لایه ها در این معماری زیاده .

میلاد رئیسی
پنج شنبه 30 خرداد 1392, 20:12 عصر
در اين درس با انواع شمارشي (Enumerator Types) در زبان سی شارپ آَشنا خواهيم شد. مطالب مورد بررسي در اين درس به شرح زير مي‌باشند :

درك و فهم يك نوع شمارشي يا يك enum
ساخت يك نوع شمارشي جديد
چگونگي استفاده از انواع شمارشي
آشنايي با متدهاي مختلف موجود در System.Enum

enmu فرم خاصي از انواع مقداري (Value Type) است كه از System.Enum مشتق شده و امكان پيمايش درون مجموعه‌اي مشخص را با استفاد از اعداد صحصيح براي ما فراهم مي‌نمايد. با استفاده از enum مي‌توان مجموعه‌اي از مقادير ثابت را تعريف نمود كه اين مقادير ثابت با استفاده از يك عدد صحيح قابل دسترسي هستند.

استفاده از enum در برنامه‌ها باعث بالا رفتن خوانايي برنامه مي‌شود، چراكه با استفاده از آنها مي‌توان با مجموعه‌اي از اعداد صحيح ترتيبي (Sequential) ، با عناويني تعريف شده، كار كرد. براي مثال، در اعلان يك enum، ما مجموعه‌اي از نامهاي مورد نظر را تعريف مي‌نماييم و در برنامه مي‌توانيم از اين نامها بصورت ترتيبي استفاده نماييم. Enum به هر يك از عناصر موجود در اين مجموعه عددي را تخصيص مي‌دهد كه شروع اين عدد مي‌تواند توسط برنامه‌نويس نيز معين گردد. سپس با استفاده از نام عناصر موجود در enum و يا با استفاده از اعدادي كه به هر يك از اين عناصر تخصيص داده شده، مي‌توان enum را پيمايش نمود و به عناصر آن دسترسي داشت.

همانطور كه گفته شد، enum يك نوع مقداري (Value Type) است، از اينرو ارث‌بري در مورد آن معنايي ندارد. مساوي قرار دادن دو enum نيز، مقادير يكي را در ديگري كپي مي‌كند. همانطور كه در اين درس، و در منابع ديگر، خواهيد يافت، دسترسي به انواع شمارشي در C# با استفاده از دو كلمه enum و Enum امكان پذير است. در C# نوع شمارشي enum از نوع BCL خود يعني Enum ارث‌بري مي‌كند ! با استفاده از enum يك نوع شمارشي جديد توليد مي‌شود و با استفاده از Enum، مي‌توان به پياده‌سازي متدهاي استاتيك انواع شمارشي پرداخت.

ايجاد يك نوع شمارشي
.Net Framework BCL حاويenum ها و مثالهاي متعددي از استفادة آنها مي‌باشد. براي مثال هرگاه كه از MessageBox بر روي فرمي استفاده مي‌شود، مي‌توان از MessageBoxIcon كه يك نوع شمارشي است استفاده نمود.

علاوه بر انواع شمارشي تعريف شده و موجود در .Net Framework، زمانهايي نيز وجود دارند كه مي‌خواهيم مجموعه‌اي از عناصر را تعريف كرده و از آنها استفاده نماييم. براي دسترسي به عناصري از نوع صحيح، استفاده از enum باعث خوانا شدن برنامه مي‌گردد.
نحوه اعلان يك enum در حالت كلي بصورت زير است :

<modifier> enum <enum_name>
{
// Enumeration list
{


در مثال 1-17 كه در زير ملاحظه مي‌كنيد، نحوه اعلان و استفاده از enum مشخص شده است.

مثال 1-17 : نحوه اعلان يك enum

using System;

// declares the enum
public enum Volume
{
Low,
Medium,
High
}

// demonstrates how to use the enum

class EnumSwitch
{
static void Main()
{
// create and initialize
// instance of enum type
Volume myVolume = Volume.Medium;

// make decision based
// on enum value
switch (myVolume)
{
case Volume.Low:
Console.WriteLine("The volume has been turned Down.");
break;
case Volume.Medium:
Console.WriteLine("The volume is in the middle.");
break;
case Volume.High:
Console.WriteLine("The volume has been turned up.");
break;
}
Console.ReadLine();
}
}


در مثال 1-17 نمونه‌اي از اعلان يك enum را مشاهده مي‌نماييد. همانطور كه ديده مي‌شود، اعلان يك نوع شمارشي با استفاده از كلمه كليدي enum صورت گرفته و سپس به دنبال آن نام اين مجموعه مشخص مي‌شود. درون كروشه‌هاي باز و بسته { } نيز، عناصر نوع شمارشي اعلان مي‌گردند.

نوع شمارشي توليد شده در اين مثال از نوع Volume است و در متد Main() از آن براي اعلان myVolume استفاده شده است. از آنجائيكه enum يك نوع مقداري است، مي‌توانيم بطور مستقيم آنرا مقداردهي نماييم. پس از آنكه متغير myVolume مقداردهي شد، مي‌توان همانند ساير انواع مقداري، مانند int، از آن استفاده نمود. در حلقه switch، متغير myVolume با عناصر enum مقايسه مي‌شوند.

در هربار استفاده از عناصر enum توليد شده، از نام enum توليد شده، در اينجا Volume، در ابتداي نام عنصر استفاده مي‌نماييم (Volume.Medium)، زيرا در صورتيكه در يك برنامه چندين enum وجود داشته باشند كه داراي عناصري با نامهاي يكسان باشند، در صورت عدم استفاده از نام enum مورد نظر قبل از عنصر، ابهام بوجود آمده و دچار مشكل مي‌شويم.

بطور پيش فرض، به اولين عنصر تعريف شده در enum مقدار صفر تخصيص داده مي‌شود كه اين مقدار تنها بعنوان انديسي جهت دسترسي به اين عنصر در نظر گرفته مي‌شود. ساير عناصر enum نيز بطور صعودي مقدار مي‌گيرند و به هر عنصر يك واحد افزوده مي‌شود. در مثال 1-17، عنصر Low داراي انديس صفر بوده و ساير عناصر به ترتيب مقدار 1 و 2 خواهند داشت.

در C#، براي موارد خاص مي‌توان مقادير پيش فرض در نظر گرفته شده براي عناصر enum را تغيير داد. به مثال زير توجه كنيد.

enum Months
{
jan, feb, mar, apr
}

enum Months
{
jan = 10, feb = 20, mar = 30, apr=40
}


همنطور كه مشاهده مي‌شود، در اعلان اول، از مقدار پيش فرض استفاده شده، كه در اين حالت jan = 0، feb = 1، mar = 2 و apr = 3 خواهند بود. اما در اعلان دوم، برنامه‌نويس بنا به نياز خود، تشخيص داده تا به هر يك از عناصر enum مقداري دلخواه را نسبت دهد.

هر چند به تمامي عناصر enum مقداري نسبت داده مي‌شود ولي از اين مقدار نمي‌توان بطور مسقيم در تخصيص دهي مقدار به متغيري ديگر استفاده نمود. به مثال زير توجه نماييد :
int x = Months.jan;// اين دستور نادرست است
int x = (int) Months.jan ; //صحيح
براي استفاده از مقدار تخصيص داده شده به عناصر enum، بايد از Casting استفاده نماييم. بدين معنا كه بايد نوع متغييري را كه مي‌خواهيم مقدار را به ان نسبت دهيم، بايد مشخص شود. در مثال فوق (int) Months.jan معين مي‌كند كه مقدار تخصيص داده شده به jan به متغييري نسبت داده مي‌شود كه از نوع int است و يا به عبارت صحيح تر، مقدار تخصيص داده شده به عنصر enum، در فرمت int به متغيير مورد نظر تخصيص داده مي‌شود.

در ادامه مبحث، توجه شما را به مثالی دیگر درباره enum جلب می نمایم. توجه نمایید که نکات جدیدی نیز در این مثال گنجانده شده اند.

مثال 2-17 : ساخت enum پایه و تخصیص دهی اعضای آن

using System;

// declares the enum
public enum Volume : byte
{
Low = 1,
Medium,
High
}

class EnumBaseAndMembers
{
static void Main()
{
// create and initialize
// instance of enum type
Volume myVolume = Volume.Low;

// make decision based
// on enum value
switch (myVolume)
{
case Volume.Low:
Console.WriteLine("The volume has been turned Down.");
break;
case Volume.Medium:
Console.WriteLine("The volume is in the middle.");
break;
case Volume.High:
Console.WriteLine("The volume has been turned up.");
break;
}
Console.ReadLine();
}
}

با توجه به مثال 2-17 با نحوه تغییر نوع پایه یک enum آشنا می شوید. همانطور که ملاحظه می نمایید، نوع پایه این enum به byte تغییر یافته است. این امر بیان میدارئ که تنها مقادیری از نوع byte قابل تخصیص به عناصر enum هستند.

همانطور که قبلا نیز اشاره شد، مقدار پیش فرض برای اولین عضو enum یعنی Low برابر با صفر است. چون در این مثال مقدار Low را برابر با یک قرار داده ایم، از اینرو مقادیر دو عضو دیگر آن نیز بصورت Middle=2 و High=3 تغییر خواهند یافت.

نکات پیشرفته درباره enum

در زبان C# هر نمونه از enum فضایی معادل با 4 بایت از حافظه را اشغال می نمایند. این مطلب با استفاده از کلمه کلیدی sizeof قابل بررسی لسا. از اینرو از enum میتوان به عنوان یک ساختمان داده مناسب و کارا یاد کرد.
نکته بسیار مهم و جالب در مورد enum در زبان برنامه نویسی C# انست که، برای هر کلاس enum موجود در کد برنامه، مقادیر رشته ای تخصیص داده شده به عناصر enum در یک اسمبلی و بصورت Metadata ذخیره می گردند، از اینرو دسترسی به این مقادیر رشته ای در کد میسر می شود. همچنین می توان از متدهای مختلف مرتبط با enum نیز استفاده نمود. به مثال ساده زیر توجه نمایید :

enum Language
{
CSharp,MCpp,VBNet,JScript,IL
}
class App
{
public static void Main()
{
Console.WriteLine("Write the number of the selected Language");
string[] langAr = Enum.GetNames(Type.GetType("Language"));
for(int i=0;i<langAr.Length;i++)
{
Console.WriteLine(i + "." + langAr[i]);
}
Language myLang=(Language)Convert.ToInt32(Console.ReadLine( ));
Console.WriteLine("Your Language of choice is: " + myLang);
}
}

میلاد رئیسی
پنج شنبه 30 خرداد 1392, 20:14 عصر
در اين درس به بررسي چند ريختي در زبان ‍C#‎‎ خواهيم پرداخت. اهداف اين درس عبارتند از :

• چند ريختي چيست؟
• پياده‌سازي متد مجازي (Virtual Method)
• Override كردن متد مجازي
• استفاده از چند ريختي در برنامه‌ها

يكي ديگر از مفاهيم پايه‌اي در شي‌گرايي، چند ريختي (Polymorphism) است. با استفاده از اين ويژگي، مي‌توان براي متد كلاس مشتق شده پياده‌سازي متفاوتي از پياده‌سازي متد كلاس پايه ايجاد نمود. اين ويژگي در جايي مناسب است كه مي‌خواهيد گروهي از اشيا‌ء را به يك آرايه تخصيص دهيد و سپس از متد هر يك از آنها را استفاده كنيد. اين اشياء الزاما نبايد از يك نوع شي‌ء باشند. هرچند اگر اين اشياء بواسطه ارث‌بري به يكديگر مرتبت باشند، مي‌توان آنها را بعنوان انواع ارث‌بري شده به آرايه اضافه نمود. اگر هر يك از اين اشياء داراي متدي با نام مشترك باشند، آنگاه مي‌توان هر يك از آنها را جداگانه پياده‌سازي و استفاده نمود. در اين درس با چگونگي انجام اين عمل آشنا مي‌گرديد.

متد مجازي (Virtual Method)

using System;

public class DrawingObject
{
public virtual void Draw()
{
Console.WriteLine("I'm just a generic drawing object.");
}
}

مثال 1-9 كلاس DrawingObject را نشان مي‌دهد. اين كلاس مي‌تواند بعنوان كلاسي پايه چهت كلاسهاي ديگر در نظر گرفته شود. اين كلاس تنها داراي يك متد با نام Draw() مي‌باشد. اين متد داراي پيشوند virtual است. وجود كلمه virtual بيان مي‌دارد كه كلاسهاي مشتق شده از اين كلاس مي‌توانند، اين متد را override نماييد و آنرا به طريقه دلخواه پياده‌سازي كنند.

using System;

public class Line : DrawingObject
{
public override void Draw()
{
Console.WriteLine("I'm a Line.");
}
}
public class Circle : DrawingObject
{
public override void Draw()
{
Console.WriteLine("I'm a Circle.");
}
}
public class Square : DrawingObject
{
public override void Draw()
{
Console.WriteLine("I'm a Square.");
}
}
در مثال 2-9، سه كلاس ديده مي‌شود. اين كلاسها از كلاس DrawingObject ارث‌بري مي‌كنند. هر يك از اين كلاسها داراي متد Draw() هستند و تمامي آنها داراي پيشوند override مي‌باشند. وجود كلمه كليدي override قبل از نام متد، اين امكان را فراهم مي‌نمايد تا كلاس، متد كلاس پايه‌ خود را override كرده و آنرا به طرز دلخواه پياده‌سازي نمايد. متدهاي override شده بايد داراي نوع و پارامترهاي مشابه متد كلاس پايه باشند.

پياده‌سازي چند ريختي

using System;

public class DrawDemo
{
public static int Main( )
{
DrawingObject[] dObj = new DrawingObject[4];
dObj[0] = new Line();
dObj[1] = new Circle();
dObj[2] = new Square();
dObj[3] = new DrawingObject();
foreach (DrawingObject drawObj in dObj)
{
drawObj.Draw();
}
return 0;
}
}

مثال 3-9 برنامه‌اي را نشان مي‌دهد كه از كلاسهاي مثال 1-9 و 2-9 استفاده مي‌كند. در اين برنامه چند ريختي پياده‌سازي شده است. در متد Main() يك آرايه ايجاد شده است. عناصر اين آرايه از نوع DrawingObject تعريف شده است. اين آرايه dObj نامگذاري شده و چهار عضو از نوع DrawingObject را در خود نگه مي‌دارد.

سپس آرايه dObj تخصيص‌دهي شده است. به دليل رابطه ارث‌بري اين عناصر با كلاس DrawingObject، عناصر Line، Circle و Square قابل تخصيص به اين آرايه مي‌باشند. بدون استفاده از اين قابليت، قابليت ارث‌بري، براي هر يك از اين عناصر بايد آرايه‌اي جدا مي‌ساختيد. ارث‌بري باعث مي‌شود تا كلاسهاي مشتق شده بتوانند همانند كلاس پايه خود عمل كنند كه اين قابليت باعث صرفه‌جويي در وقت و هزينه توليد برنامه مي‌گردد.

پس از تخصيص‌دهي آرايه، حلقه foreach تك تك عناصر آنرا پيمايش مي كند. درون حلقه foreach متد Draw() براي هر يك از اعضاي آرايه اجرا مي‌شود. نوع شيء مرجع آرايه dObj، DrawingObject است. چون متد Draw() در هر يك از اين اشياء override مي‌شوند، از اينرو متد Draw() مربوط به هر يك از اين اشياء اجرا مي‌شوند. خروجي اين برنامه بصورت زير است :

I'm a Line.
I'm a Circle.
I'm a Square.
I'm just a generic drawing object.

متد override شده Draw() مربوط به هر يك از كلاسهاي مشتق شده در برنامه فوق همانند خروجي اجرا مي‌شوند. آخرين ط خروجي نيز مربوط به كلاس مجازي Draw() از كلاس DrawingObject است، زيرا آخرين عنصر آرايه شيء DrawingObject است.

خلاصه
در اين پست با مفهوم كلي چند ريختي آشنا شديد. چند ريختي امكاني است كه مخصوص زبان‌هاي برنامه‌نويسي شي‌گرا است و از طريق آن مي‌توان براي يك متد موجود در كلاس پايه، چندين پياده‌سازي متفاوت در كلاسهاي مشتق شده داشت.

farazjalili
پنج شنبه 30 خرداد 1392, 20:43 عصر
با عرض سلام و خسته نباشید به دوست عزیز شما که زحمت آموزش شی گرا رو کشیدن یک زحمت بکشید مثال هاتون بیشتر real word تر باشن به عنوان مثال چند ریختی رو چه جوری توی یک برنامه MIS ساده مثل کتابخونه و یا .... پیاده سازی کنیم و یا مباحث دیگر مثل وراثت

میلاد رئیسی
سه شنبه 04 تیر 1392, 09:08 صبح
با عرض سلام و خسته نباشید به دوست عزیز شما که زحمت آموزش شی گرا رو کشیدن یک زحمت بکشید مثال هاتون بیشتر real word تر باشن به عنوان مثال چند ریختی رو چه جوری توی یک برنامه MIS ساده مثل کتابخونه و یا .... پیاده سازی کنیم و یا مباحث دیگر مثل وراثت

با سلام . در حال حاضر بسیار درگیر مسائل کاری هستم . در اسرع وقت براتون یه مثال جامع میسازم !