درس نهم _ چند ریختی (Polymorphism)
در این درس به بررسی چند ریختی در زبان 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 است.
خلاصه
در این درس با مفهوم کلی چند ریختی آشنا شدید. چند ریختی امکانی است که مخصوص زبانهای برنامهنویسی شیگرا است و از طریق آن میتوان برای یک متد موجود در کلاس پایه، چندین پیادهسازی متفاوت در کلاسهای مشتق شده داشت.
درس هشتم – ارثبری کلاسها
در این درس درباره ارثبری در زبان برنامهنویسی C# صحبت خواهیم کرد. اهداف این درس بشرح زیر میباشند :
ü پیادهسازی کلاسهای پایه (Base Class)
ü پیادهسازی کلاسهای مشتق شده (Derived Class)
ü مقدار دهی کلاس پایه از طریق کلاس مشتق شده
ü فراخوانی اعضای کلاس پایه
ü پنهانسازی اعضای کلاس پایه
ارثبری یکی از مفاهیم اساسی و پایه شیگرایی است. با استفاده از این ویژگی امکان استفاده مجدد از کد موجود فراهم میشود. بوسیله استفاده موثر از این ویژگی کار برنامهنویسی آسانتر میگردد.
ارثبری(Inheritance)
using System;
public class ParentClass
{
public ParentClass()
{
Console.WriteLine("Parent Constructor.");
}
public void print()
{
Console.WriteLine("I'm a Parent Class.");
}
}
public class ChildClass : ParentClass
{
public ChildClass()
{
Console.WriteLine("Child Constructor.");
}
public static void Main()
{
ChildClass child = new ChildClass();
child.print();
}
}
خروجی این برنامه بصورت زیر است :
Parent Constructor.
Child Constructor.
I'm a Parent Class.
در مثال 1-8 دو کلاس وجود دارد. کلاس بالای ParentClass و کلاس پائینی ChildClass است. کاری که میخواهیم در اینجا انجام دهیم اینست که زیر کلاسی ایجاد کنیم که با استفاده از کدهای موجود در ParentClass عمل نماید.
برای این منظور ابتدا باید در اعلان ChildClass مشخص کنیم که این کلاس میخواهد از کلاس ParentClass ارثبری داشته باشد. این عمل با اعلان public class ChildClass : ParentClass روی میدهد. کلاس پایه با قرار دادن ":" بعد از نام کلاس مشتق شده معین میشود.
C# فقط از ارثبری یگانه پشتیبانی مینماید. از اینرو تنها یک کلاس پایه برای ارثبری میتوان معین نمود. البته باید اشاره کرد که ارثبری چندگانه تنها از واسطها (Interfaces) امکانپذیر است که در درسهای آینده به آنها اشاره مینماییم.
ChildClass دقیقاً توانائیهای ParentClass را دارد. از اینرو میتوان گفت ChildClass یک ParentClass است. (ChildClass IS a ParentClass) ChildClass دارای متد Print() مربوط به خود نیست و از متد کلاس ParentClass استفاده میکند. نتیجه این عمل در خط سوم خروجی دیده میشود.
کلاسهای پایه به طور خودکار، قبل از کلاسهای مشتق شده نمونهای از روی آنها ایجاد میگردد. به خروجی مثال 1-8 توجه نمایید. سازنده ParentClass قبل از سازنده ChildClass اجرا میگردد.
برقراری ارتباط کلاس مشتق شده با کلاس پایه
به مثال 2-8 که در زیر آمده است توجه نمایید.
using System;
public class Parent
{
string parentString;
public Parent()
{
Console.WriteLine("Parent Constructor.");
}
public Parent(string myString)
{
parentString = myString;
Console.WriteLine(parentString);
}
public void print()
{
Console.WriteLine("I'm a Parent Class.");
}
}
public class Child : Parent
{
public Child() : base("From Derived")
{
Console.WriteLine("Child Constructor.");
}
public new void print()
{
base.print();
Console.WriteLine("I'm a Child Class.");
}
public static void Main()
{
Child child = new Child();
child.print();
((Parent)child).print();
}
}
خروجی این برنامه بشکل زیر است :
From Derived
Child Constructor.
I'm a Parent Class.
I'm a Child Class.
I'm a Parent Class.
کلاسهای مشتق شده در طول ایجاد نمونه میتوانند با کلاس پایه خود ارتباط برقرار نمایند. در مثال 2-8 چگونگی انجام این عمل را در سازنده ChildClass نشان میدهد. استفاده از " : " و کلمه کلیدی base باعث فراخوانی سازنده کلاس پایه به همراه لیست پارامترهایش میشود. اولین سطر خروجی، فراخوانی سازنده کلاس پایه را بهمراه رشته "From Derived" نشان میدهد.
ممکن است حالتی رخ دهد که نیاز داشته باشید تا متد موجود در کلاس پایه را خود پیادهسازی نمایید. کلاس Child این عمل را با اعلان متد Print() مربوط به خود انجام میدهد. متد Print() مربوط به کلاس Child، متد Print() کلاس Parent را پنهان میکند. نتیجه این کار آنست که متد Print() کلاس Parent() تا زمانیکه عمل خاصی انجام ندهیم قابل فراخوانی نمیباشد.
درون متد Print() کلاس Child، صریحاً متد Print() کلاس Parent را فراخوانی کردهایم. این عمل با استفاده از کلمه کلیدی base قبل از نام متد انجام گرفته است. با استفاده از کلمه کلیدی base میتوان به هر یک از اعضای public و protected کلاس پایه دسترسی داشت. خروجی مربوط به متد Print() کلاس Child در سطرها سوم و چهارم خروجی دیده میشوند.
روش دیگر دسترسی به اعضای کلاش پایه، استفاده از Casting صریح است. این عمل در آخرین سطر از متد Main() کلاس Child رخ داده است. توجه داشته باشید که کلاس مشتق شده نوع خاصی از کلاس پایهاش میباشد. این مسئله باعث میشود تا بتوان کلاس مشتق شده را مورد عمل Casting قرار داد و آنرا نمونهای از کلاس پایهاش قرار داد. آخرین خط خروجی نشان میدهد که متد Print() کلاس Parent اجرا شده است.
به وجود کلمه کلیدی new در متد Print() کلاس Child توجه نمایید. این عمل باعث میشود تا متد Print() کلاس Child متد Print() کلاس پایهاش را پنهان نماید. درصورتیکه از کلمه کلیدی new استفاده نشود، کامپایلر پیغام اخطاری را توالید میکند تا توجه شما را به این مسئله جلب کند. توضیحات بیشتر در این زمینه مربوط به مبحث چندریختی (Polymorphism) است که در درس آینده آنرا بررسی خواهیم نمود.
خلاصه
در این درس با روش ایجاد کلاس پایه و کلاس مشتق شده از آن آشنا شدید. متدهای کلای پایه را میتوانید بصورت صریح و یا ضمنی فراخوانی کنید. همچنین متوجه شدید که کلاس مشتق شده نوع خاصی از کلاس پایه است.
درس هفتم – آشنایی با کلاسها در C#
در این درس با کلاسها در زبان C# آشنا خواهید شد. اهداف این درس به شرح زیر میباشند :
ü پیادهسازی سازندهها (Constructors)
ü درک تفاوت بین اعضای نمونه (Instance) و استاتیک (Static)
ü آشنایی با تخریب کنندهها (Destructors)
ü آشنایی با اعضای کلاسها
در تمامی مطالبی که در این سایت مشاهده کردهاید، برنامهها دارای کلاسهایی بودهاند. در حال حاضر باید درک نسبی از کلاسها و کار آنها و چگونگی ایجاد آنها داشته باشید. در این درس مروری بر آموختههای قبلی از کلاسها خواهیم کرد و نیز با اعضای کلاسها آشنا میشویم.
یک کلاس با استفاده از کلمه کلیدی class که بدنبال آن نام کلاس آمده باشد، اعلان میگردد و اعضای این کلاس درون {} اعلان میگردند. هر کلاس دارای سازندهای میباشد که در هربار ایجاد نمونهای جدید از آن کلاس، بصورت خودکار فراخوانی میگردد. هدف از سازنده، تخصیصدهی اعضای کلاس در زمان ایجاد نمونهای جدید از کلاس است. سازندهها دارای مقادیر بازگشتی نبوده و همواره نامی مشابه نام کلاس دارند. مثال 1-7 نمونهای از یک کلاس را نشان میدهد.
// Namespace اعلان
using System;
class OutputClass
{
string myString;
// سازنده
public OutputClass(string inputString)
{
myString = inputString;
}
// متد نمونه
public void printString()
{
Console.WriteLine("{0}", myString);
}
// تخریب کننده
~OutputClass()
{
// روتینی جهت آزادسازی برخی از منابع سیستم
}
}
// کلاس آغازین برنامه
class ExampleClass
{
// آغاز اجرای برنامه
public static void Main()
{
// OutputClass نمونهای از
OutputClass outCl = new OutputClass("This is printed by the output class.");
// Output فراخوانی متد کلاس
outCl.printString();
}
}
در مثال 1-7 دو کلاس دیده میشوند. کلاس بالایی، کلاس OutPutClass، دارای سازنده، متد نمونه و یک تخریب کننده است. همچنین این کلاس دارای فیلدی با نام myString است. توجه نمایید که چگونه سازنده این کلاس اعضای آنرا تخصیصدهی(مقداردهی) مینماید. در این مثال، سازنده کلاس رشته ورودی (inputString) را بعنوان آرگومان خود دریافت مینماید. سپس این مقدار داخل فیلد کلاس یعنی myString کپی میگردد.
همانطور که در ExampleClass مشاهده مینمایید، استفاده از سازنده الزامی نمیباشد. در این مورد سازنده پیش فرض ایجاد میگردد. سازنده پیش فرض، سازندهای بدون هیچ نوع آرگومانی است. البته شایان ذکر است که سازندههاییی بدون آرگومان همیشه مورد استفاده نبوده و مفید نیستند. جهت کارآمد کردن بیشتر سازندههای بدون آرگومان بهتر است آنها را با تخصیصدهنده (Initializers) پیادهسازی نمایید. به مثال زیر در این زمینه توجه نمایید :
public OutputClass() : this("Default Constructor String") { }
فرض کنید این عبارت در کلاس OutPutClass در مثال 1-7 قرار داشت. این سازنده پیش فرض به یک تخصیصدهنده همراه شده است. ":" ابتدای تخصیصدهنده را مشخص مینماید، و به دنبال آن کلمه کلیدی this آمده است. کلمه کلیدی this به شیء کنونی اشاره مینماید. استفاده از این کلمه، فراخوانی به سازنده شیء کنونی که در آن تعریف شده است، ایجاد میکند. بعد از کلمه کلیدی this لیست پارامترها قرار میگیرد که در اینجا یک رشته است. عملی که تخصیصدهنده فوق انجام میدهد، باعث میشود تا سازنده OutPutClass رشتهای را بعنوان آرگومان دریافت نماید. استفاده از تخصیصدهندهها تضمین مینمایند که فیلدهای کلاس شما در هنگام ایجاد نمونهای جدید مقداردهی میشوند.
مثال فوق نشان داد که چگونه یک کلاس میتواند سازندههای متفاوتی داشته باشد. سازندهای که فراخوانی میشود، به تعداد و نوع آرگومانهایش وابسته است.
در زبان C#، اعضای کلاسها دو نوع میباشند : اعضای نمونه و استاتیک. اعضای نمونه کلاس متعلق به رخداد خاصی از کلاس هستند. هربار که شیای از کلاسی خاص ایجاد میکنید، در حقیقت نمونه جدیدی از آن کلاس ایجاد کردهاید. متد Main() در کلاس ExampleClass نمونه جدیدی از OutPutClass را تحت نام outCl ایجاد مینماید. میتوان نمونههای متفاوتی از کلاس OutPutClass را با نامهای مختلف ایجاد نمود. هر یک از این نمونههای مجزا بوده و به تنهایی عمل میکنند. به عنوان مثال اگر دو نمونه از کلاس OutPutClass همانند زیر ایجاد نمایید :
OutputClass oc1 = new OutputClass("OutputClass1");
OutputClass oc2 = new OutputClass("OutputClass2");
با این اعلان، شما دو نمونه از کلاس OutPutClass را ایجاد کردهاید که یک از آنها دارای فیلد myString و متد printString() هستند و این فیلدها و متدها کاملاً از یکدیگر مجزا میباشند. به بیان دیگر درصورتیکه عضوی از کلاس استاتیک باشد از طریق ساختار نوشتاری
public static void staticPrinter()
{
Console.WriteLine("There is only one of me.");
}
این متد را از درون متد Main() به صورت زیر میتوانید فراخوانی نمایید :
OutputClass.staticPrinter();
اعضای استاتیک یک کلاس تنها از طریق نام آن کلاس قابل دسترس میباشند و نه از طریق نام نمونه ایجاد شده از روی کلاس. بدین ترتیب برای فراخوانی اعضای استاتیک یک کلاس نیازی به ایجاد نمونه از روی آن کلاس نمیباشد. همچنین تنها یک کپی از عضو استاتیک کلاس، در طول برنامه موجود میباشد. یک مورد استفاده مناسب از اعضای استاتیک در مواردی است که تنها یک عمل باید انجام گیرد و در انجام این عمل هیچ حالت میانی وجود نداشته باشد، مانند محاسبات ریاضی. در حقیقت، .Net Framework BCL خود دارای کلاس Math میباشد که از اعضای استاتیک بهره میبرد.
نوع دیگر سازندهها، سازندههای استاتیک هستند. از سازندههای استاتیک جهت مقداردهی فیلدهای استاتیک یک کلاس استفاده میشود. برای اعلان یک سازنده استاتیک تنها کافیست که از کلمه کلیدی static در جلوی نام سازنده استفاده نمایید. سازنده استاتیک قبل از ایجاد نمونه جدیدی از کلاس، قبل از فراخوانی عضو استاتیک و قبل از فراخوانی سازنده استاتیک کلاس مشتق شده، فراخوانی میگردد. این سازندهها تنها یکبار فراخوانی میشوند.
OutPutClass همچنین دارای یک تخریبکننده (Destructor) است. تخریبکنندهها شبیه به سازندهها هستند، با این تفاوت که در جلوی خود علامت "~" را دارا میباشند. هیچ پارامتری دریافت نکرده و هیچ مقداری باز نمیگردانند. از تخریبکنندهها میتوان در هر نقطه از برنامه که نیاز به آزادسازی منابع سیستم که در اختیار کلاس یا برنامه است، استفاده نمود. تخریبکنندهها معمولاً زمانی فراخوانی میشوند که Garbage Collector زبان C# تصمیم به حذف شیء مورد استفاده برنامه از حافظه و آزادسازی منابع سیستم، گرفته باشد. (Garbage Collector یا GC، یکی از امکانات .Net Framework مخصوص زبان C# است که سیستم بصورت اتوماتیک اقدام به آزادسازی حافظه و باز گرداندن منابع بلا استفاده به سیستم مینماید. فراخوانی GC بصورت خودکار رخ میدهد مگر برنامهنویس بصورت صریح از طریق تخریبکنندهها آنرا فراخوانی نماید. در مباحث پیشرفتهتری که در آینده مطرح میکنیم خواهید دید که در چه مواقعی نیاز به فراخوانی تخریبکنندهها بصورت شخصی دارید.)
تا کنون، تنها اعضای کلاس که با آنها سر و کار داشتهاید، متدها، فیلدها، سازندهها و تخریبکنندهها بودهاند در زیر لیست کاملی از انواعی را که میتوانید در کلاس از آنها استفاده نمایید آورده شده است :
• Constructors
• Destructors
• Fields
• Methods
• Properties
• Indexers
• Delegates
• Events
• Nested Classes
مواردی که در این درس با آنها آشنا نشدید، حتماً در درسهای آینده مورد بررسی قرار خواهند گرفت.
خلاصه
در این درس نحوه اعلان سازندههای استاتیک و نمونه را فرا گرفتید و با نحوه مقداردهی به فیلدها آشنا شدید. زمانیکه نیاز به ایجاد نمونه از روی شیء نباشد از اعضای استاتیک کلاس استفاده میکنیم. با استفاده از تخریبکنندهها میتوانید منابع بلا استفاده را به سیستم باز گردانید.