درس دهم – ویژگیها در C#
در این درس با ویژگیها (Properties) در زبان C# آشنا خواهیم شد. اهداف این درس به شرح زیر میباشد :
Property ها امکان ایجاد حفاظت از فیلدهای یک کلاس را از طریق خواندن و نوشتن بوسیله Property را فراهم مینماید. Property ها علاوه بر اینکه از فیلدهای یک کلاس حفاظت میکنند، همانند یک فیلد قابل دسترسی هستند. بمنظور درک ارزش Property ها بهتر است ابتدا به روش کلاسیک کپسوله کردن متدها توجه نمایید.
مثال 1-10 : یک نمونه از چگونگی دسترسی به فیلدهای کلاس به طریقه کلاسیک
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-10 روش کلاسیک دسترسی به فیلدهای یک کلاس را نشان میدهد. کلاس 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-10 : دسترسی به فیلدهای کلاس به استفاده از 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-10 چگونگی ایجاد و استفاده از ویژگیها (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-10 : ویژگیهای فقط خواندنی
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-10 چگونگی ایجاد یک ویژگی فقط خواندنی را نشان میدهد. کلاس 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-10 : ویژگیهای فقط خواندنی
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-10 چگونگی ایجاد و استفاده از ویژگی فقط نوشتنی را نشان میدهد. در این حالت get accessor را از ویژگی SomeProperty حذف کرده و به جای آن set accessor را قرار دادهایم.
متد Main() کلاس PropertyTester شیای جدید از همین کلاس با سازندة پیش فرض آن ایجاد مینماید. سپس با استفاده از ویژگی SomeProperty از شیء propHold، مقدار 5 را به فیلد someProperty مربوط به شیء propHold تخصیص میدهد. در این حالت set accessor مربوط به ویژگی SomeProperty فراخوانی شده و مقدار 5 را به فیلد someProperty تخصیص میدهد و سپس عبارت someProperty is equal to 5” “را در خروجی نمایش میدهد.
خلاصه
در این درس با ویژگیها آشنا شدید و نحوه استفاده از آنها را فرا گرفتید. روشهای کلاسیک کپسوله کردن از طریق استفاده از متدهای مجزا صورت میگرفت ولی با استفاده از ویژگیها (Property) میتوان به اجزای یک شیء همانند یک فیلد دسترسی پیدا کرد. ویژگیها را میتوان به صورت فقط خواندنی و یا فقط نوشتنی نیز ایجاد نمود. با استفاده از ویژگیها دسترسی مستقیم به فیلدهای مورد نظر از یک کلاس از بین رفته و این دسترسی تنها از طریق ویژگی مورد نظر امکانپذیر میگردد.
درس نهم _ چند ریختی (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) است که در درس آینده آنرا بررسی خواهیم نمود.
خلاصه
در این درس با روش ایجاد کلاس پایه و کلاس مشتق شده از آن آشنا شدید. متدهای کلای پایه را میتوانید بصورت صریح و یا ضمنی فراخوانی کنید. همچنین متوجه شدید که کلاس مشتق شده نوع خاصی از کلاس پایه است.