توجه ! این یک نسخه آرشیو شده می باشد و در این حالت شما عکسی را مشاهده نمی کنید برای مشاهده کامل متن و عکسها بر روی لینک مقابل کلیک کنید : آموزش جامع تصویری و کاربردی سی شارپ (#c)
sunyboy
08-10-2009, 10:43 AM
سي شارپ در سال 2001 توسط شركت مايكروسافت به همراه بسته دات نت براي اولين بار مطرح و ارائه شد. كه بعد ها به عنوان يك زبان برنامه نويسي استاندارد توسط ECMA و ISO مورد تائيد قرار گرفت.
زبان برنامه نويسي سي شارپ توسط تيمي به مديريت اندرز هايلزبرگ كه قبلا تجربه ارائه زبان هاي برنامه نويسي موفقي همچون توربو پاسكال و دلفي رو داشت ايجاد شد. هايلزبرگ سي شارپ را يك زبان برنامه نويسي شي گراء كه از زبان هاي برنامه نويسي بزرگ (همچون دلفي , جاوا و اسمال تاك) تاثير پذيري داشته معرفي مي كنه. Syntax زبان سي شارپ شبيه به ++C مي باشد و البته تفاوت هاي بسياري هم داره.
حالا قبل از اينكه بخواهم بيشتر در مورد سي شارپ توضيح بدم بايد يه توضيحاتي هم در مورد دات نت و امكاناتش بگم.
دات نت فريم ورك (NET Framework) يه بسته نرم افزاري شامل راه حل بسياري از نيازهاي توسعه نرم افزاري (Base Class Library) و همچنين امكان توسعه و اجراي برنامه هاي توليد شده براي اين فريم ورك مي باشد. برنامه هاي توليد شده براي دات نت فريم ورك توسط Common Language Runtime اجراء مي شوند.
CLR يا همان Common Language Runtime سرويس هايي مهمي از قبيل Garbage Collection , Memory Managment و Exception Handling رو ارائه مي دهد.
Garbage Collection: در زبان برنامه نويسي سي شارپ ديگر نگراني از جهت مديريت اشياء در حافظه وجود ندارد به اين معني كه وقتي شما شيء ايجاد مي كنيد بعد از اينكه ديگر از آن شي ء استفاده نكرديد آن شيء به طور اتوماتيك از حافظه پاك خواهد شد. اين كار توسط GC يا همان Garbage Collector انجام مي شود.
روش كار GC به اين ترتيب است كه تا موقعي كه Referenceي به يك object وجود داشته باشد آن شيء در حافظه باقي خواهد ماند اما در صورتيكه Referenceي به آن شيء وجود نداشته باشد بعد از يك بازه زماني نامشخص آن شيء به صورت اتوماتيك از حافظه پاك خواهد شد. در مورد نحوه عملكردش بعدا بيشتر توضيح ميدم.
يه نكته ديگري كه بايد بهش اشاره كنم اينه كه برخلاف زبان هاي برنامه نويسي ديگر وقتي كد شما در زبان برنامه نويسي سي شارپ (يا هريك از زبان هاي ديگر دات نت) كامپايل مي شود به يك زبان ديگري به نام Intermediate Language يا همان IL تبديل مي شود. و در موقعي كه درخواست براي اجراء آن داده مي شود توسط يك مكانيزمي به نام Just In Time Compiler كه در CLR موجود است به زبان خاص آن ماشين تبديل شده و اجراء مي گردد.
http://www.u2u.net/Res/articles/images/msdnet11.gif
sunyboy
08-10-2009, 10:44 AM
حالا مي خوام در مورد سي شارپ كمي بيشتر صحبت كنيم. سي شارپ يك زبان شيء گراست به اين معني كه هرآنچه كه در سي شارپ وجود دارد در غالب دو مفهوم كلاس (Class) و شيء (Object) و روابط بين آنها خلاصه مي شود.
كلاس(Class): در واقع يك ايده (Concept) يا ذهنيت مي باشد. مثلا يك نجار چه ذهنيتي نصبت به يك "ميز" دارد؟ خوب مسلما يه صفحه , چهار پايه و .... اين ذهنيتي است كه يك نجار نسبت به "ميز" دارد. اين دقيقا مفهوم كلاس است. يعني تا موقعي كه آن ميز ساخته نشه فقط يك ذهنيت يا در واقع كلاس است.
شيء (Object): اشياء موجوديت هايي هستند كه از كلاس ها (ذهنيت) ساخته مي شوند. باز براي مثال همان نجار وقتي ذهنيتش رو به واقعيت تبديل مي كنه در واقع يك ميز مي سازه يا يك Object (شيء) ايجاد كرده است.
حالا اگر يكم دقت كنيم مي بينيم كه توي دنياي واقعي هم همينطوره. مفهوم انسان يك كلاس است و وقتي يك نفر متولد مي شود يك شيء از آن كلاس داريم.
باز وقتي بيشتر دقت مي كنيم مي بينيم كه همه كلاس ها (ذهنيت ها) مي تونن يكسري مشخصات داشته باشند. مثلا انسان. مسلما هر انساني اسم داره , سن داره , رنگ مو , رنگ پوست و ... اين اطلاعات state يا خصوصيات هر فرد هستند. و البته هر انساني يكسري توانمندي داره يعني مي تونه يكسري كار انجام بده. مثلا راه بره , حرف بزنه , گوش كنه , بنويسه , .... اين ها رفتار ها يا behavior هر فرد هستند.
حالا سوالي كه پيش مياد اينه كه آيا ميشه دو نفر با خصوصيات مشترك و رفتار مشترك رو يكي دونست؟
علي احمدي 33 ساله - علي احمدي 33 ساله؟
آيا به نظر شما اين دو يك نفر هستند!؟!
خير. پس هميشه يك چيز وجود داره كه ما ميتونيم دو نفر رو از هم تميز بديم: ما به اون مي گيم Identity (وسيله شناسايي). براي اين دو مي تونه شماره شناسنامشون باشه يا كد ملي يا ...
حالا به نظر شما براي كامپيوتر دو موجود كه داراي اطلاعات و رفتار هاي كاملا مشترك هستند چطور از هم تشخيص داده مي شوند؟ جواب محل قرار گيريشان در حافظه است.
sunyboy
08-10-2009, 10:44 AM
كلاس هاي پايه دات نت - dot Net Base Class Library
بعد از اينكه مفهوم كلاس و شيء رو متوجه شديم. براي اطلاعتون بايد بگم كه دات نت فريم ورك حدود 2500 كلاس داره كه قبلا براي شما نوشته شده و شما بدون اينكه احتياجي به نوشتنشون داشته باشين خيلي راحت مي تونين از اين 2500 كلاس موجود استفاده كنين.
Namespace چيست؟
اگر يك مقدار فكر كنيم مي بينيم كه پيدا كردن يك كلاس بين 2500 كلاس پايه و احتمالا كلاس هايي كه خودتون نام گذاري شون مي كنين كار سختي خواهد بود!
Namespace ها در حقيقت اين امكان رو بشما مي دهند كه بتوانيد كلاس هاي خودتون رو دسته بندي كنين و هر كلاس رو داخل دسته مورد نظر خودشون قرار بدين. به عنوان مثال ما كلاسي داريم به نام SqlConnection و همينطور كلاس ديگري به نام SqlDataAdapter و .... براي همين يك Namespace به نام System.Data.SqlClient ايجاد شده كه كلاس هايي كه مربوط به System و كار با داده ها (Data) و در نهايت مختص Sql Server هستند رو داخل اين namespace قرار مي دهيم. توي اين تصوير تعدادي از namespace هاي عمومي دات نت نمايش داده شده اند.
http://msdn2.microsoft.com/en-us/library/Aa446549.net_vs_netcf02%28en-us,MSDN.10%29.gif
اين نكته رو هم بياد داشته باشين كه اسم كامل يك كلاس شامل اسم به همراه نام namespace آن است:
System.Data.SqlClient.SqlConnection اسم كامل ( Qualified Name) كلاس SqlConnection مي باشد.
using
اما اگر قرار باشد براي استفاده از يك كلاس هميشه اسم كاملش رو بنويسيم كار ما خيلي سخت مي شود. براي همين شما ميتوانيد با يكبار نوشتن اسم Namespace آن كلاس (يا كلاس هايي كه مي خواهيد استفاده كنين) با استفاده از يك keyword به نام using از تكرار آن جلوگيري كنين.
به عنوان مثال من در يك مثال مي خواهم ده ها بار از كلاس Console و دستوراتش استفاده كنم. براي همين بالاي كدم يك بار using System; رو ذكر مي كنم تا از نوشتن كلمه System براي دفعات مكرر جلوگيري كنم:
using System;
sunyboy
08-10-2009, 10:46 AM
اجازه بدين كمي از دنياي تئوري خارج شيم و كمي هم كد بنويسيم. اما قبل اينكه وارد كد نويسي بشيم بايد با محيطي كه قراره توش كد بنويسيم رو بشناسيم.
برنامه نويسان سي شارپ دات نت معمولا از Visual Studio .NET براي توليد كد استفاده مي كنند. هرچند كه در اين مورد شما هيچ محدوديتي ندارين و مي تونين از هر ابزاري حتي Notepad براي توليد كد استفاده كنين.
Visual Studio .NET 2005
نسخه 8 Visual Studio شركت مايكروسافت كه خيلي راحت مي تونين از بازار تهيه كنين و خوشبختانه (يا شايد هم متاسفانه) دو سه هزار تومان بيشتر قيمت نداره. البته به دليل قيمتي زيادي كه در بيرون از ايران داره مايكروسافت يك نسخه مجاني به نام Visual Studio 2005 Express Edition رو هم ارائه مي كنه كه مي تونين از لينك زير دانلودش كنين.
Visual Studio 2005 Express Edition
! من توي توضيحاتم از نسخه Professional استفاده خواهم كرد.
بعد از اينكه مراحل نصب تمام شد. از منوي Start --> All Programm --> Microsoft Visual Studio 2005 روي Microsoft Visual Studio 2005 كليك كنين تا پنجره مقابل براتون باز بشه. اين صفحه Start Page مي باشد.
http://www.tabatabaei.info/csharpsamples/VS2005.GIF
اگر دقت كنين سمت چپ بالا دو تا گزينه Open و Create كه امكان ايجاد يا باز كردن پروژه يا وب سايت رو به شما مي دهد دارين. روي گزينه Create Project كليك كنين(مي تونين اين كار رو با استفاده از كليد هاي Shift + Ctrl + N هم انجام بدين. بهتون پيشنهاد مي كنم براي سريع تر شدن كارتون Shortcut ها رو ياد بگيرين.
http://www.tabatabaei.info/csharpsamples/VSNewProject.GIF
در پنجره مقابل روي گزينه Visual CSharp كليك كنين تا انواع پروژه هايي كه مي شه با زبان سي شارپ توليد كرد رو بينين. در ابتدا دوره ما با پيروي از اصول مايكروسافت مثال هايمان در محيط Console Application كه يك محيطي شبيه با DOS و بدون طراحي UI مي باشد شروع خواهيم كرد. در ادامه وارد Windows و ... مي شويم.
در قسمت پائين صفحه داخل قسمت Name نام پروژه خود رو بنويسين و در قسمت Location مسير پروژه خود رو مشخص كنين و در نهايت اسم Solution رو وارد نمائيد. به صورت پيش فرض اسم Solution همان اسم پروژه شماست. دقت كنين كه در دات نت براي هر پروژه يك Folder ايجاد خواهد شد. البته در صورتيكه شما گزينه Create directory for solution رو هم تيك بزنين يك Folder هم براي Solution ايجاد خواهد شد.
Solution چيست؟
Solution يا به معناي فارسي راه حل بالاترین سطح دسته بندي در محيط ويژوال استوديو دات نت است. يك Solution مي تواند يك يا چند پروژه و هم اين طور فايل را در كنار هم دسته بندي نمايد.اصولا وظيفه Solution نگهداري اطلاعات روابط بين پروژه هاست كه يك فايل با پسوند sln مي باشد. و داراي تاثير در خروجي پروژه نخواهد بود. در ادامه دوره بيشتر در اين مورد توضيح خواهم داد.
در مقابل Project يا همان پروژه ها داراي خروجي مستقيم با توجه به نوعشان هستند. به عنوان مثال Console Application ها داراي خروجي با پسوند exe به معناي executable يا همان فايل هاي قابل اجراء مي باشند.
! در دات نت ما به خروجي هر پروژه فارغ از اينكه چه نوع فايلي است (exe يا dll) اسمبلي - Assembly مي گويم.
بعد از اينكه كليد OK رو زديد پروژه شما ايجاد مي شود و براي شما يك فايل به نام Programm.cs رو باز مي نمايد. اين فايل به صورت پيش فرض محلي است كه نرم افزار شما از داخل آن شروع خواهد شد. حالا فقط براي اينكه شروع كرده باشيم داخل اين فايل و مطابق شكل زير شروع به تايپ كردن نمائيد:
کد:
Console.WriteLine("Hello World - C# Tuning!");
http://www.tabatabaei.info/csharpsamples/HelloWorldCsharpTuning.gif
و در نهايت براي اينكه نرم افزارتون رو بتونين تست كنين كافيه كه كليد Ctrl + F5 رو بزنين تا اين نتيجه رو بگيرين:
http://www.tabatabaei.info/csharpsamples/RunHWCsharpTuning.gif
sunyboy
08-10-2009, 10:47 AM
اجازه بدين ابتدا كدي كه قبلا نوشتيم رو بررسي كنيم.
Console.WriteLine("Hello World - C# Tuning");
اين خط در حقيقت همان جمله Hello World - C# Tuning رو براي ما چاپ مي كنه. در حقيقت كلاس Console داراي يك رفتار (Method) مي باشد كه امكان چاپ بر روي صفحه رو به ما مي دهد. اين متد WriteLine مي باشد.
پس اگر شما هر چيز ديگري داخل پرانتز و بين " " قرار دهيد همان را براي شما چاپ خواهد كرد.
Method چيست؟
در واقع Method ها همان رفتار هايي هستند كه ما از كلاس ها انتظار داريم. در مثال بالا نوشتن بر روي تصوير رو مي توانيم با استفاده از متد WriteLine بر روي كلاس Console استفاده كنيم.
ايجاد متغيير ها در سي شارپ - Variables in CSharp
براي ايجاد يك متغيير در سي شارپ بايد ابتدا نوع داده اي آن و سپس نام متغيير را وارد نمائيم و در انتها ; را تايپ كنيم:
int MyNumber = 1000;
Console.WriteLine(MyNumber);
در مثال بالا از نوع داده اي int كه يك نوع داده اي عددي است استفاده شده است. پس مي توانيم مقادير عددي رو داخل اين متغيير قرار دهيم. و مثل كد بالا با استفاده از WriteLine چاپش كنيم.
نوع هاي داده اي در سي شارپ - C# DataTypes
نوع هاي داده اي رو مي توان بسته به محل قرار گيريشان در حافظه به سه دسته تقسيم كرد:
Values Types
Reference Types
Pointer Types
!!! تاكيد دارم كه اين مطلب يكي از مهمترين مباحث برنامه نويسي در سي شارپ است.
اگرحافظه رو به دو قسمت Stack و Heap تقسيم كنيم. مقادير تمامي متغيير هاي نوع اول در حافظه Stack قرار مي گيرد و به همين جهت داراي رفتارهاي خاصي مي شود كه بيشتر توضيح خواهم داد. مقادير متغيير هاي نوع دوم در حافظه Heap قرار مي گيرند. در مورد Poiter Type ها صحبت نخواهيم كرد.
Value Types: همان طور كه گفتيم مهمترين خاصيت اين نوع متغيير ها قرارگيري مقادير آن ها در حافظه Stack مي باشد كه به همين دليل رفتارهاي خاصي خواهند داشت. اين نوع متغيير ها شامل : Primitive Types يا همان نوع هاي بدوي - Enum ها و Struct ها مي باشند.
Numeric Types , Boolean , Char نوع هايي هستند كه به آن ها Primitive Types مي گوييم. نوع هاي عددي شامل : int , double , decimal , long , short , byte و ... كه مقادير مختلفي از اعداد رو داخل خوشان نگه مي دارند.
Boolean ها متغيير هايي هستند كه فقط مقدار True و False به معني مثبت يا منفي رو داخل خوشان نگه مي دارند. و در نهايت Char ها همان كاراكتر ها هستند.
bool married = false;
int myNumber = 1000;
char c = 'c';
! دقت داشته باشين كه براي مقدار دهي char از ' يا همان Single Quotation استفاده كرديم. و براي bool فقط مقدار true يا false امكان پذير است.
sunyboy
08-10-2009, 10:49 AM
گفتيم كه ValueType ها داراي رفتار خاصي هستند. اما چه رفتاري؟
وقتی یک متغییر از یکی از نوع های Value Type مثل int ایجاد می کنیم مقدار آن داخل حافظه Stack قرار می گیرد و وقتی از آن متغییر یک کپی میگیریم مقدار متغییر قبلی داخل متغییر جدید کپی می شود.
یعنی اگر مقدار متغییر دوم رو عوض کنیم تاثیری روی مقدار متغییر اول نخواهد گذاشت. این رفتار در مورد تمامی ValueType ها صدق می نماید:
http://www.tabatabaei.info/csharpsamples/p1.gif
وقتي مقدار متغيير دوم رو تغيير دهيم مقدار متغيير اول دست نخورده باقي ماند.
http://www.tabatabaei.info/csharpsamples/p2.gif
Enumerations
فرض بفرمائيد كه شما مي خواهيد يك كلاس تعريف كنيد به نام انسان. وقتي خواص انسان رو تحت بررسي دارين به جنسيت مي رسيد. حالا مي خواهيم روي كلاس انسان يك متغيير براي نشان دادن جنسيت تعريف كنيم. به نظر شما جنسيت رو از چه نوعي بايد در نظر گرفت؟ bool يا int؟
اگر bool در نظر گرفتيم false به معني مرد خواهد بود يا true؟
اگر int در نظر گرفتيم چه عددي بيانگر مرد و چه عددي بيانگر زن مي باشد؟ و اگر كاربر شما عددي به غير اعداد انتخابي شما وارد كرد, چه طور؟
گاهي اوقات در توسعه نرم افزار ها ما به جنس (Type)هايي نياز داريم كه بتوانيم مقدار شان را محدود كنيم. مثلا همين جنسيت. با استفاده از Enumeration ها مي توانيم مقدار متغيير جنسيت رو به مرد يا زن محدود كنيم.
براي ايجاد يك Enumeration بايد در يك فايل با پسوند cs كه بيانگر سي شارپ است از Syntax زير استفاده كنيم:
public enum eSex
{
Male,
Female
}
دقت كنين كه وقتي متغييري از اين جنس بسازيم به هيجوجه مقداري به غير از مرد يا زن نمي توانيم اختصاص دهيم:
eSex Sex; // ايجاد متغيير از جنس يك enum
Sex = eSex.Male; // مقدار دهي متغيير
حالا مي بينيم كه چطور با استفاده از enum مي توانيم مقدار دهي يك متغيير را به مقادير محدودي نسبت دهيم. در دات هم مثال هاي متعددي از استفاده از enum ها وجود دارد. مثلا يك enum به نام Keys تمامي كليد هاي صفحه كليد رو در خود جاي داده است. يا enumي با نام ConnectionState وضعيت هاي امكان پذير يك Connection رو نشان مي دهد.
sunyboy
08-10-2009, 10:50 AM
اجازه بدين تا مقداري موضوع بحث رو از Value Type ها به يك Reference تايپ تغيير بديم.
كلاس ها - Classes
قبل از اين تعريف كلاس رو با هم بررسي كرديم. اما حالا چطور مي توان يك كلاس توليد كرد. فرض بفرمائيد مفهومي مثل انسان رو مي خواهيم در غالب يك كلاس تعريف كنيم.
ابتدا يك پروژه جديد از نوع Console Application با يك نام دلخواه در مسير دلخواه تان ايجاد كنين. سپس يك فايل با نام Prgoramm.cs خواهيد داشت كه در مثال قبلي ديده بوديم. حالا بايد يك كلاس جديد به پروژه اضافه كنيم. براي اين كار بايد از منوي View گزينه Solution Explorer رو انتخاب كنيم يا مي توانيم از كليد هاي Alt + Ctrl + L استفاده نمائيم.
Solution Explorer در واقع يك نمايش مبنتي بر فايل از پروژه يا Solution شماست كه به صورت يك درخت واره (TreeView) مي باشد. راس آن Solution شماست و سپس پروژه و Properties و Reference و در نهايت كلاس ها و آيتم هاي ديگر پروژه شماست. روي Project يعني دومين آيتم از اين درخت واره راست كليك كرده و گزينه Add و بعد New Item رو كليك مي نمائيم. در پنجره اي كه باز مي شود يك Class را انتخاب و در قسمت پايين اسم فايل رو مشخص مي نمائيم. دقت كنين كه پسوند فايل هاي سي شارپ cs مي باشد. (براي اين مثال نام Person رو تايپ نمائيد.)
! نكته اي كه لازمه در نامگذاري فايل ها متدها و ... رعايت كنين اين است كه نام گذاري كلاس ها بهتر است به ترتيبي باشد كه حرف اول هر كلمه با حروف بزرگ (Upper Case) و ما بقي با حروف كوچك باشد. به عنوان مثال كلاس Person كه P با حروف بزرگ و ما بقي كوچك مي باشد. يا كلاس SqlConnection حروف اول هر كلمه يعني S و C با Upper case نوشته مي شوند.
وقتي اسم رو تايپ كردين و گزينه Add رو زديد. سپس يك فايل به Solution Explorer اضافه خواهد شد و همزمان براي شما نمايش داده مي شود. متن داخل فايل به اين ترتيب است:
using System;
using System.Collections.Generic;
using System.Text;
namespace CSharpTuning.Samples
{
class Person
{
}
}
قبل از اين در مورد كلمه using و namespace صحبت كرده ايم. همانطور كه مي دانيد يك namespace يك دسته بندي براي كلاس هاست. حالا ما داريم يك namespace به نام CSharpTuning.Samples براي مثال هاي اين وبلاگ توليد مي كنيم و كلاس Person را داخل اين namespace قرار مي دهيم. دقت كنين كه هر namespace داراي يك block از كد مي باشد كه با استفاده از علامت } شروع و بعد با { پايان مي يابد و هر آنچه كه داخل اين علامت باشد داخل آن namespace قرار خواهد گرفت.
ايجاد كلاس در سي شارپ
براي ايجاد يك كلاس در سي شارپ شما بايد داخل يك namespace از كلمه كليدي class و سپس نام كلاس استفاده كنين. مثال با براي ايجاد كلاس Person داخل namespaceي به نام CSharptuning.Samples جمله فوق يعني class Person رو به همراه يك block از كد براي اين كلاس ايجاد مي كنيم. دقت كنين كه شما تا به اينجا دو علامت } و دو علامت { ديده ايد يعني شما دو block از كد داريد كه يكي براي namespace و ديگري براي كلاس Person مي باشد. هر آنچه كه داخل {} علامت مربوط به كلاس Person قرار گيرد متعلق به كلاس Person است.
فيلد ها - Fileds در سي شارپ
تا ايجاي كار كلاس ما هنوز هيچ عضوي ندارد اما ما مي توانيم از اين كلاس استفاده كنيم. حالا بايد داخل اين كلاس اطلاعات مورد نظرمان را تعريف كنيم. من مي خواهم Field هاي Name و Age رو براي اين كلاس تعريف كنم. به اين معنا كه هر انسان (Person) داراي نام و سن مي باشد.
براي تعريف يك Field با داخل block كلاس مورد نظر ابتدا كلمه public و سپس نوع داده اي و سپس نام متغيير را وارد نمائيم. پس من داخل block كلاس Person اين دو جمله را تايپ مي نمايم.
public string Name;
public int Age;
توجه داشته باشين كه فعلا فقط از كلمه public استفاده مي نمائيم. در آينده در مورد ما بقي Access Modifier ها صحبت مي كنيم.
پس تا اينجا كلاس ما بايد به اين شكل باشد:
http://www.tabatabaei.info/csharpsamples/ClassPerson.JPG
حالا مي توانيم از اين كلاس شي ء بسازيم و به اشيايي كه ايجاد كرديم مقادير Name و Age رو ست كنيم.
sunyboy
08-10-2009, 10:54 AM
بعد از اينكه يك كلاس رو ايجاد كرديم بايد بتوانيم از آن كلاس استفاده كنيم. اصولا براي استفاده از يك كلاس مي توانيم از آن كلاس يك شيء بسازيم و سپس به آن شيء اطللاعات ست كنيم و از رفتار هاي آن كلاس استفاده كنيم. همانطور كه قبلا هم گفتم حافظه رو مي توان به دو قسمت Stack و Heap تقسيم كرد. براي ساختن يك شيء (object) بايد ابتدا يك متغيير از جنس كلاس مورد نظر ايجاد كنيم:
Person p;
در حقيقت متغيير p يك رابطه (Reference) به شيء مورد نظر خواهد بود. پس يك شيء رو به آن متغيير نسبت مي دهيم. دقت داشته باشين كه reference شما در حاقظه Stack و خود شيء شما در حافظه Heap ساخته خواهد شد:
p = new Person();
البته در تمامي پست ها من براي اينكه بتوانم به شيء مورد نظرم دسترسي داشته باشم reference رو يك object صدا خواهم زد امادقت داشته باشين كه در حقيقت p يك refernce است و نه objcet اما بدون آن reference ما به آن object دسترسي نخواهيم داشت (اگرobject رو يك تلويزوين در يك محلي عمومي مثل فرودگاه در نظر بگيرين بدون داشتن remote control نمي توانين كانال تلويزيون رو عوض كنين. پس اين reference يا متغيير p در نقش يك remote control براي شيء است كه ما در حافظه Heap داريم).
خطوط بالا رو مي توان در يك خط خلاصه كرد و همان موقع كه يك reference مي سازيم همان موقع هم با يك object جديد مقدار دهي نمائيم:
Person p = new Person();
p.Name = "Ali";
p.Age = 20;
به شكل زير دقت كنين:
http://www.tabatabaei.info/csharpsamples/objectinitilization.GIF
اين در واقع اتفاقي است كه با توجه به خط بالا مي افتد , يعني يك متغيير در حافظه Stack و يك شيء در حافظه Heap. اما با استفاده reference كه داريم (p) مي توانيم مقادير object مورد نظرمون رو ست كنيم و ...
دقت داشته باشين كه وقتي شما از كلمه new و سپس اسم كلاس با استفاده از پرانتز استفاده مي كنين, در واقع يك شيء جديد در حافظه ساخته مي شود و اگر اين شيء رو به يك متغيير مثل p كه قبلا ساخته شده بود مقدار دهي كنين يك رابطه جديد و در نتيجه مقاديرتان را از دست خواهيد داد:
Person x = new Person();
x.Name = "hossein";
x.Age = 44;
x = new Person();
Console.WriteLine(x.Age); // خروجي اين خط صفر خواهد بود
x.Name = "hossein";
شكل زير اتفاقاتي كه در خطوط با مي افتد را تفسير مي نمايد:
http://www.tabatabaei.info/csharpsamples/GarbageCollection.GIF
دقت نمائيد كه objectي كه مقادير Reza و 44 را داشته است توسط Garbage Collection به دلايلي كه قبلا هم توضيح داديم از حافظه حذف خواهد شد.
حالا با توجه به مثال هايي كه صحبت شد به نظر شما خروجي خطوط زير چه خواهد بود؟
myPerson = new Person();
myPerson.Name = "Masoud";
myPerson.Age = 33;
Person yourPerson = myPerson;
yourPerson.Name = "hossein";
yourPerson.Age = 23;
Console.WriteLine("myPerson Info: Name:{0}, Age:{1}",myPerson.Name,myPerson.Age);
Console.WriteLine("yourPerson Info: Name:{0}, Age:{1}",yourPerson.Name,yourPerson.Age);
خروجي كه اين چند خط كد دارند بايد به شكل زير باشد:
myPerson Info: hossein, Age: 23
yourPerson Info: hossein, Age : 23
در صورتيكه متوجه دليلش نمي شويد به شكل زير توجه كنين:
http://www.tabatabaei.info/csharpsamples/referencecopy.GIF
sunyboy
08-10-2009, 10:59 AM
Reference كپي در سي شارپ
اجازه بدين كمي بيشتر در مورد اين خط كد توضيح بديم:
Person yourPerson = myPerson;
در حقيقت در كدي كه نوشته شده يك كپي از يك شيء به نام myPerson ايجاد شده و نام آن را yourPerson نهاده ايم. دقت كنين كه در سي شارپ رفتار پيش فرض كپي از اشياء reference copy است. همانطور كه در آخرين تصوير پست قبلي (http://www.tabatabaei.info/csharpsamples/referencecopy.GIF) ديديم , وقتي من از myPerson يك Reference copy تهيه مي كنم در حقيقت فقط آدرس حافظه myPerson رو در متغيير yourPerson قرار مي دهم و نه خود شيء را. البته اين يك نوع از انواع كپي ها (نوع پيشفرض) مي باشد در پست هاي آينده در مورد انواع كپي صحبت خواهيم كرد.
Place Holders
Person p = new Person();
p.Name = "hossein";
p.Age = 20;
Console.WriteLine("MyName is {0} and I 'm {1} years old.",p.Name,p.Age);
اگر به كدي كه در خط بالا نوشته ايم دقت كنين يك سري عدد داخل متن رشته اي كه در متد WriteLine استفاده شده است مي بينيند. اعداد در داخل دو علامت {} قرار گرفته اند و از 0 تا n مي باشند. در واقع اين اعداد جاينگهدار (PlaceHolder)هايي هستند كه بايد با مقادير متغيير هايي كه به ترتيب بعد از متن رشته اي قرار مي گيرند جايگزين شوند. در واقع خروجي كد بالا متن زير خواهد بود. كه متغيير p.Name يا همان "Ali" جايگزين {0} شده و مقدار متغيير p.Age كه 20 بود نيز جايگزين {1} شده است.
MyName is Ali and I 'm 20 years old.
متد ها در سي شارپ - Methods in CSharp
همانطور كه در ابتداي صحبتمان در مورد كلاس ها صحبت كرديم در واقع رفتار هاي كلاس ها (يا همان اشياء شان) از طريق متد ها پياده سازي مي گردد. به اين معني كه اگر كلاس Person توانمندي چاپ اطلاعاتش را نياز داشته باشد بايد يك متد براي اين كار ايجاد كند.
براي ايجاد يك متد در سي شارپ كافي است از Syntax زير استفاده كنيم:
AccessModifier returnType MethodName([parameter type parameter name])
{
}
در مورد Access Modifier ها در پست هاي بعدي به تفضيل صحبت خواهيم كرد اما فعلا ما از public يا همان عمومي استفاده خواهيم كرد.
در صورتيكه متد شما بايد مقدار برگرداند (شبيه function ها در vb) بايد جنس متغيير خروجي را مشخص كنيم. مثلا اگر متد شما دو عدد رو با هم جمع مي زند و يك حاصل رو در غالب يك عدد بر مي گرداند نوع آن را int تعريف مي كنيم. اگر متد شما خروجي ندارد و فقط يك سري كار را انجام مي دهد (subroutine ها در vb) نوع خروجي آن را void تعريف مي كنيم.
MethodName كه اسم متد مورد نظر شماست.
در صورتيكه متد شما داراي ورودي (parameter) است كافيه كه به ترتيب نوع داده اي پارامتر و سپس نام آن را تايپ مي كنيم. اگر تعداد پارامتر ها بيش از يكي است از , براي جدا كردن آن ها استفاده مي كنيم.
دقت كنين كه هر متد داراي يك block of code براي خودش مي باشد. پس حتما {} رو قرار مي دهيم و كد هايي كه نياز داريم را ما بين اين دو علامت مي نويسيم.
! محل قرار گيري متد ها در block of code كلاس هاست.
حالا به متد ShowInfo كه براي نمايش اطلاعات اشخاص در داخل كلاس Person مي نويسم دقت كنين:
public void ShowInfo()
{
Console.WriteLine("Name: {0},Age: {1}",Name,Age);
}
براي استفاده از متد ها هم كافيه كه بعد از اسم شيء (object) نام متد را به همراه پرانتز استفاده كنيم. دقت كنين كه اگر متد شما داراي ورودي است مقادير ورودي داخل پرانتز قرار مي گيرد.
Person p = new Person();
p.Name = "hossein";
p.age = 20;
p.ShowInfo();
يك مثال هم از متدي كه دو عدد را گرفته و حاصل جمعشان را بر ميگرداند:
publc class Calc
{
public int Add(int num1,int num2)
{
return num1 + num2;
}
}
و استفاده از آن:
Calc c= new Calc();
int result = c.Add(10,50);
Console.WriteLine("Result of {0} + {1} is: {2}",10,50,result);
در مورد متد ها در پست هاي بعدي بيشتر صحبت خواهيم كرد.
sunyboy
08-10-2009, 11:55 AM
توي آخرين پست در مورد متد ها صحبت كرديم اما اگر دقت كرده بوديد متوجه مي شديد كه ما تا اينجا از يك متد استفاده كرده ايم كه ننوشته ايم! به كد زير دقت كنين:
Person p = new Person();
همانطور كه قبلا هم گفتم براي استفاده از متد ها كافيه اسم متد رو با استفاده از () بنويسيم. كد بالا نيز اسم يك متد به نام ()Person ذكر شده است:
سازنده ها در سي شارپ - Constructors in CSharp
سازنده (Constructor) متدي است هم نام با كلاس كه چه بنويسيم (تايپ كنيم) و چه ننويسيم بر روي كلاس ها وجود دارد. همانطور كه قبلا هم ديديد من براي كلاس Person متدي هم نام با كلاس ننوشتم اما مي توانستم از اين متد در كنار كلمه new استفاده كنيم. كلمه new فقط براي ايجاد كردن يك شيء در حافظه در كنار نام متد استفاده مي شود. پس دقت داشته باشين كه وقتي ما يك reference copy از يك شيء تهيه ميكنيم در واقع فقط يك كپي از آدرس حافظه همان شيء را داريم (مثل 2 ريموت كنترل به يك تلويزويون) , اما وقتي از new به همراه اسم متد استفاده مي كنيم يك شيء جديد در حافظه داريم.
سازنده پيشفرض - Default Constructor
سازنده پيش فرض يا همان Default Constructor متدي است هم نام با كلاس كه پارامتر ورودي ندارد و شما به صورت پيش فرض يك ورژن از سازنده ها رو در كلاستان دارين. به اين معني كه حتي وقتي كلاس شما داراي هيچ عضوي نيست (مثل اين كلاس Emp ) باز داراي يك متد سازنده يا همان Constructor مي باشد. البته اين به اين معني نيست كه شما نمي توانيد سازنده ها را تايپ كنين:
public class Emp
{
}
public class Student
{
public Student()
{
// default constructor
}
}
[code]
در هر دو كلاس بالا شما داراي default constructor مي باشد پس مي توانين كه كد هاي زير را تايپ كنين:
Emp e = new Emp();
Student st = new Student();
نكته بسيار مهم استفاده است كه شما مي توانين از Constructor ها داشته باشيد. دقت كنين كه وقتي شما كدي را داخل block of code يك سازنده از يك كلاس مي نويسيد مادامي كه از اين كلاس شيء جديد ساخته مي شود كدي كه داخل constructor نوشته شده نيز اجرا خواهد شد در نتيجه شما با استفاده از سازنده ها اين توانمندي را خواهيد داشت كه در زمان ساخته شده هر شيء كد مورد نظرتان را اجراء نمائيد.
فرض كنين كه من مي خواهم هر زماني كه يك object از كلاس Emp ساخته شد يك جمله در محيط كنسول چاپ شود كه يك object جديد ساخته شد. پس:
public class Emp
{
public Emp()
{
Console.WriteLine("New Emp Object Created ...");
}
}
Constructor Overloading
در سي شارپ شما اين امكان را داريد كه ورژن هاي متفاوتي از يك متد را داشته باشين. از آنجايي كه سازنده ها هم به نوعي متد محسوب مي شوند شما مي توانيد ورژن هاي متفاوتي از سازنده ها را داشته باشين. براي مثال در نظر بگيرين كه من مي خواهم يك ورژن از سازنده براي كلاس Person بنويسم كه وقتي از اين كلاس شيء ايجاد مي شود حتما نام و سن فرد ذكر شود. پس براي اينكار سازنده اي تعريف مي كنم كه داراي دو ورودي , يكي از جنس رشته اي (براي نام) و ديگري از جنس عددي (براي سن) داشته باشد:
public class Person
{
public Person(string name, int Age)
{
Name = name;
Age = age;
}
public int Age;
public string Name;
}
! دقت داشته باشين كه وقتي شما يك ورژن ديگر از سازنده را مي نويسن (ورژني به غير از سازنده پيش فرض) در حالتيكه سازنده پيش فرض را تايپ نكنين ديگر امكان استفاده از سازنده پيش فرض را نداريد.
به همين دليل در صورتيكه شما كد بالا را بنويسين ديگر امكان استفاده از سازنده پيش فرض كلاس Person وجود نخواهد داشت و اين به اين معني است كه شما به برنامه نويس اجبار خواهيد كرد كه حتما موقع ساختن شيء از كلاس شما نام و سن شيء را مشخص كند.
//You will get compile time error if you uncomment the next line
//Person p = new Person();
Person p =new Person("Ali",20);
در صورتيكه امكان ايجاد كردن شيء از اين كلاس بدون مشخص كردن نام و سن را نياز داريد مي توانين سازنده پيش فرض را نيز تايپ نمائيد.
نقش هاي يك برنامه نويس - Programmer 's Role
1. Class Programmer
2. Class Creator
هر برنامه نويس در حين نوشتن كد مي تواند دو نقش داشته باشد و بسيار مهم است كه نقش خود را در هر لحظه بتواند تشخيص دهد.
فرض كنين كه شما در حال نوشتن كلاس Emp هستين :
public class Emp
{
...
در اين حالت شما يك Class Creator مي باشيد. به اين معني كه شما يك كلاس را ايجاد مي كنين فرقي نمي كند كه شما از اين كلاس استفاده كنين يا كس ديگري.
در حالتي كه شما در كلاس Programm هستيد و داخل متد Main از كلاس Empاستفاده مي كنين شما يك Class Programm براي كلاس Emp هستيد يعني از اين كلاس استفاده مي كنين. دقت كنين كه در همين لحظه شما براي كلاس Programm يك Class Creator مي باشين.
درك كردن اين نقش ها به شما كمك خواهد كرد كه ديد بهتري نسبت به توليد كلاس ها داشته باشين و متوجه باشين كه چه چيزي رو در اختيار چه نقشي قرار مي دهيد.
sunyboy
08-10-2009, 11:57 AM
حالا كه در مورد Constrcutor ها و Method ها صحبت كرديم مي تونيم بگيم كه تا حدودي كلاس ها رو شناختيم و حالا مي تونيم ادامه مطالبمون در رابطه با Value Type ها رو پيش ببريم:
Structures in CSharp
Structure ها نوع هاي داده اي هستند شبيه به كلاس ها به اين معنا كه مي توانند Field و Method و Constructor داشته باشند و حتي در بعضي از موارد به خاطر نوع رفتاري كه Value Type ها دارند به جاي كلاس ها استفاده شوند. براي تعريف يك Structure كافيست در جايي از namespace با استفاده از keyword ي كه به همين منظور ايجاد شده است يعني struct شروع به ايجادش نمائيم:
public struct Teacher
{
public int Age;
public string Name;
public void Print()
{
Console.WriteLine("Name: {0}, Age: {1}",Name, Age);
}
}
همانطور كه مي بينيم ساختار ظاهري Struct ها كاملا شبيه به كلاس است اما تفاوت اصلي آن ها در رفتارهايشان مي باشد.
در تعريف رفتار هاي كلاس گفتيم (http://csharptuning.blogfa.com/post-12.aspx) وقتي شما يك شيء از نوع يك كلاس مثل Person ايجاد مي كنين و بعد از آن شيء كپي تهيه مي كنين فقط آدرس حافظه شيء قبلي به شيء جديد اختصاص يافته و در حقيقت شما فقط يك شيء داريد.
اما در مورد Struct ها جريان كاملا شبيه به كپي گرفتن از تايپ هاي بدوي (Primitive Types) ها مي باشد. به چند خط كد زير دقت كنين:
Teacher t = new Teacher();
t.Name = "Ali";
t.Age = 44;
Teacher t2 = t;
t2.Name = "Reza";
t.Print();
t2.Print();
به نظر شما خروجي اين چند خط كد چيست؟
به اشكال زير دقت كنين:
http://www.tabatabaei.info/csharpsamples/Struct-CSharp.GIF
http://www.tabatabaei.info/csharpsamples/Struct-CSharp2.GIF
نكته بسيار مهم در شكل اول اينست كه اصولا به خاطر Value Type بودن Struct ها هيچ شيء اي در حافظه Heap ايجاد نشده است و فقط دو متغيير در حافظه Stack ايجاد شده است. نكته مهم دوم اينه كه وقتي شما از يك متغيير از نوع Struct كپي مي گيرين تمامي مقادير موجود در آن Struct كپي شده و در متغيير جديد يك كپي از آن مقادير قرار خواهد گرفت. پس در نتيجه تغييراتي كه ما در متغيير دوم يعني t2 داديم برروي متغيير اول يا همان t تاثيري نخواهد داشت.
Constructors in Structures - سازنده ها در ساختار ها
نكته مهم ديگري كه مي توان در مورد Struct ها بيان كرد در مورد سازنده ها در اين جنس است. در پست قبلي در مورد ايجاد سازنده ها در كلاس ها صحبت كرديم و گفتم كه شما مي توانين ورژن هاي مختلفي از سازنده ها را در يك كلاس داشته باشين. اما بايد بگويم كه در مورد Struct ها جريان كمي متفاوت خواهد بود.
نكته اول: شما سازنده پيشفرض را در Struct ها دارين اما امكان تايپ كردن آن را به صورت دستي ندارين. به اين معني كه اگر شما يك Constructor بدون پارامتر در يك Struct تعريف كنين به Compile Time Error بر خورد خواهيد كرد و در حقيقت شما نمي تونين هيچ تغييري در رفتار سازنده پيشفرض Struct ها ايجاد كنين. بايد بدونين كه سازنده پيش فرض در Structureها در حقيقت يك object ايجاد نمي كند بلكه به تمامي متغييرهايي كه داخل Struct شما وجود دارند مقدار پيش فرض را Set مي كند.
Teacher t = new Teacher();
در نتيجه كد بالا فيلد Age مقدار 0 و فيلد Name مقدار "" را كه مقادير پيشفرض int و String هستند را خواهند گرفت.
نكته دوم: در تمامي ورژن هاي سازنده ها بايد تمامي متغيير هاي Struct شما مقدار دهي شوند. در غير اينصورت بازهم Compile Time Error خواهين داشت.
در نتيجه Struct شما مي تواند به اين شكل باشد:
public struct Teacher
{
public int Age;
public string Name;
public void Print()
{
Console.WriteLine("Name: {0},Age: {1}",Name,Age);
}
public Teacher(int age, string Name)
{
Name = name;
Age = age;
}
public Teacher(int age)
{
Age = age;
Name = "";
}
}
sunyboy
08-10-2009, 11:58 AM
خوب البته صحبت ما در مورد Structure ها هنوز تمام نشده اما اجازه بدين ادامه مطلب رو بعد از اينكه كلاس ها رو بيشتر بررسي كرديم داشته باشيم.
نوع هاي در سي شارپ - Reference Type in CSharp
مهم ترين نوع داده اي Reference Type در سي شارپ همان Class يا كلاس ها مي باشند كه تا حدودي در موردشان صحبت كرديم. اما يك نوع داده اي ديگر به نام object وجود دارد كه بايد در موردش صحبت كنيم.
اصولا object خود نيز يك كلاس است اما به جهت اهميتي كه دارد من آن را به صورت جدا از بقيه كلاس ها بررسي مي نمايم. همانطور كه قبلا هم گفتم سي شارپ يك زبان Object Oreinted يا همان شيء گراست و تمامي مفاهيم شيء گرايي در اين زبان رعايت مي شود. يكي از مهمترين مفاهيم شيء گرايي مفهوم Inheritance يا همان توارث مي باشد. توارث در حقيقت به معني به ارث رفتن خصوصيات يك موجود از موجود ديگر مي باشد. و نكته اي كه Inheritance رو به object ربط مي دهد اين است كه object به عنوان base class تمامي كلاس هاي موجود در دات نت فريم ورك مي باشد. به اين معني كه تمامي كلاس هايي كه در دات نت فريم ورك و كلاس هايي كه شما مي نويسيد, همه و همه از كلاس object به ارث رفته اند.
وقتي يك كلاس از يك كلاس ديگر به ارث مي رود تمامي خصوصيات عمومي آن نيز به آن كلاس به ارث مي رود. مثلا اگر من كلاسي به نام Person داشته باشم كه داراي اطلاعات Name و Age و متد Print باشد وقتي كه كلاس Student را از كلاس Person به ارث مي برم خصوصيات عمومي كلاس Person در كلاس Student نيز قابل استفاده مي باشد.
به مثال زير دقت كنيد:
public class Test
{
}
با توجه به كدي كه بالا نوشته شده است در اين كلاس هيج موجودي (اعم از Field يا Method و يا ...) وجود ندارد اما اگر از اين كلاس يك شيء بسازيم متوجه مي شويم كه در اين كلاس يك سري متد وجود دارد:
http://www.tabatabaei.info/csharpsamples/personclass.gif
نكته قابل تامل اينجاست كه اين چهار متد (ToString(), GetHashCode(), GetType(),Equal) همگي در تمام كلاس هايي كه در دات نت يافت مي شود وجود دارد. پس مي توان نتيجه گرفت كه اين متد ها از كلاس object به همه كلاس ها به ارث مي رسند.
اين كه هر كدام از اين متد ها چه كاري انجام مي دهند را بعد ها بيشتر توضيح خواهم داد.
sunyboy
08-10-2009, 12:03 PM
رشته ها در سي شارپ - String in CSharp
نوع داده اي رشته اي نيز يكي از مهم ترين نوع هاي داده ايست كه در اين گروه قرار مي گيرد(Reference Types). براي ايجاد يك متغيير از نوع داده اي رشته اي مي توانيم از نمونه كد زير استفاده كنيم:
string myName = "ali";
همانطور كه در كد بالا مشاهده مي كنيد براي مقدار دهي يك متغيير از نوع داده اي رشته اي كافيست مقدار مورد نظرمان را داخل دو علامت "" قرار داده و با استفاده از = مقدار دهي را انجام دهيم.
myName += "reza";
با توجه به كد بالا مقدار myName با استفاده از operation += كه در حقيقت مقدار قبلي را + مقدار جديد كرده و مقدار دهي مي نمايد از Ali به alireza تغيير يافت. البته شما مي توانستيد اين كد را به صورت زير نيست بنويسيد:
myName = myName + "Reza";
البته دليل ذكر مثال با روش استفاده از =+ نيست بلكه مي خواهم در مورد رفتار كلاس string كمي توضيح دهم.
در حقيقت كلاس string يك كلاس از نوع Reference Type هاست اما به دليل استفاده بسيار زيادش در زبان هاي برنامه نويسي مايكروسافت روش استفاده از آن را با استفاده keyword ي با همان نام و با حروف كوچك يعني string آسانتر و به روش value type ها نموده است. به عبارت ديگر وقتي شما مي نويسيد:
string myName = "ali";
در واقع كامپايلر يك شيء از نوع string در حافظه Heap براي شما ايجاد مي كند:
http://www.tabatabaei.info/csharpsamples/String-CSharp.GIF
و وقتي مقدار قبلي را با مقدار جديد "Reza" جمع مي كنين يك شيء كاملا جديد در حافظه ايجاد مي شود. و شيء قبلي توسط Garbage Colletion از حافظه پاك خواهد شد.
http://www.tabatabaei.info/csharpsamples/String-CSharp2.GIF
با توجه به نكته بالا اگر در شرايطي نياز داشتيد تا يك متن را مرتبا تغيير دهيد بهترين روش استفاده از string ها نخواهد بود. چرا كه هر چه تعداد دفعات تغيير متن شما بيشتر باشد به همان ميزان تعداد object هاي ساخته شده در حافظه Heap نيز زياد خواهد شد (البته كه Garbage Collection آن ها را حذف خواهد كرد) اما بهتر از كلاس ديگري به نام StringBuilder براي اين موضوع استفاده شود. تا به بهترين نحو از حافظه سيستم استفاده كنيم. كلاس StringBuilder در namespace System.Text قرار گرفته است و براي استفاده از آن بايد يك شيء از آن بسازيم:
StringBuilder st = new StringBuilder();
و بعد مي توانيم با استفاده از متد Append متن مورد نظرمان را به آن اضافه كنيم:
st.Append("Ali");
st.Append("Reza");
و در نهايت با استفاده از متد ()ToString نتيجه را در غالب يك String در اختيار بگيريم:
string Name = st.ToString();
رفتار StringBuilder برخلاف رفتار String مي باشد و به اين ترتيب خواهد بود كه در ابتدا مثلا 16 بايت حافظه براي خودش در نظر خواهد گرفت. وقتي نصف اين 16 بايت پر شده (با استفاده از دستور Append - يعني 8 بايت) آنوقت خودش به صورت اتوماتيك يك شيء جديد خ با دو برابر اندازه فعلي در حافظه خواهد ساخت (يعني 32 بايت) سپس مقادير قبلي را داخل اين شيء جديد كپي مي گيرد. و باز وقتي نصف اين 32 بايت پر شد به همين ترتيب عمل خواهد كرد. نتيجتا به صورت تساعدي اين مقدار حافظه بيشتر مي شود و در نتيجه تعداد اشيايي كه در حافظه ساخته مي شوند كمتر و كمتر خواهد بود و در نتيجه سرعت عملكرد آن به مراتب سريعتر خواهد بود.
متد ()ToString
! دقت داشته باشين كه متد ToString از كلاس object به تمامي كلاس هاي دات نت به ارث مي رسد. در نتيجه ميتوانيد از اين متد هر جا كه به شكل رشته اي يك شيء نياز داشتين استفاده كنين:
int i = 12;
int j = 32;
string myResult = (i * j).ToString();
البته دقت كنين كه هميشه اون چيزي انتظار دارين رو به شما بر نخواهد گرداند:به عنوان مثال وقتي از شيء اي از كلاس Person را ToString كنيم (يا هر كلاسي كه شما ايجاد كرده باشيد) به صورت پيشفرض خروجي ToString آن اسم كامل (Qualified Name) آن خواهد بود:
Person p = new Person();
p.Name = "Ali";
p.Age = 20;
Console.WriteLine(p.ToString());
يعني اگر كلاس Person من در اين مثال داخل namespaceي به نام ConsoleApplication12 باشد خروجي اين مثال ConsoleApplication12.Person خواهد بود.
sunyboy
08-10-2009, 12:08 PM
آرايه ها در سي شارپ - Arrays in CSharp
مسلما به مانند بيشتر زبان هاي برنامه نويسي استفاده از آرايه ها در سي شارپ نيز مرسوم است. استفاده از آرايه ها براي نگهداري چندين مقدار از يك نوع داده اي استفاده مي شود.
آرايه ها را مي توان از يك ديدگاه به سه دسته تقسيم كرد:
1. آرايه هاي تك بعدي - Simple Arrays
2. آرايه هاي مستطيلي - Rectangular Arrays
3. آرايه هاي نامنتظم - Jagged Arrays
براي تعريف يك آرايه تك بعدي در سي شارپ مي توانيد از Syntax زير استفاده كنين:
numbers = new int[3];
numbers[0] = 100;
numbers[1] =400;
numbers[2] = 500;
[code]
همانطور كه مي بينين در كد بالا من يك آرايه از اعداد ايجاد كردم كه داراي سه خانه مي باشد. براي دسترسي به هر كدام از خانه هاي اين آرايه بعد از نام متغييرم از [] استفاده مي كنم و بين اين دو علامت از يك عدد (indexer) كه از صفر تا n مي باشد. دقت كنين كه هميشه n يكي كمتر از طول آرايه شماست.
البته به غير از كدي كه بالا نوشته شده است من مي توانم از هر يك از روش هاي زير نيز براي ايجاد آرايه هاي استفاده كنم:
[CODE]
int[] numbers = new int[]{100,400,500};
در روش بالا بدون ذكر طول آرايه با مقدار دهي مستقيم آن به وسيله مقاديري كه بين دو علامت {} قرار مي گيرند طول آن را مشخص مي نمائيم.
اين هم يك نمونه ديگر از ايجاد آرايه ها:
int numbers = {100,400,500};
نكته قابل تامل در مورد آرايه ها اين است كه اين نوع هاي داده اي در دسته Reference Type ها قرار مي گيرند و اصولا مقادير آن ها در حافظه Heap نگهداري مي شود:
http://www.tabatabaei.info/csharpsamples/Arrays%20in%20CSharp.GIF
حالا اگر من يك آرايه از يك كلاس مثلا Person داشته باشم به نظر شما شكل حافظه اين آرايه من چگونه خواهد بود؟ به كد زير دقت كنين و سعي كنين كه شكل حافظه آن را رسم نمائيد:
Person[] personList = new Person[3];
دقت كنين كه حتما تمامي خانه هاي اين آرايه من بايد قبل از اينكه مورد استفاده بگيرند مقدار دهي شوند:
personList[0] = new Person("Ali",24);
personList[1] = new Person("Reza",35);
personList[2] = new Person("John",44);
كه در نتيجه خطوط با شكل حافظه ما به اين ترتيب خواهد بود:
http://www.tabatabaei.info/csharpsamples/Arrays%20in%20CSharp%20Ref.GIF
نكته بسيار مهم ديگر اين است كه ساختار آرايه ها به نحوي است كه طول آن ها ثابت مي باشد و در صورتيكه شما بخواهيد طول آن را تغيير دهيد مثلا از 3 خانه به 5 خانه ارتقاء دهيد بايد يكبار ديگر آن را new كنيد و در نتيجه اين new كردن شما اطلاعات قبلي خود را از دست خواهيد داد.
personList = new Person[5];
و نتيجه كد بالا اين شكل خواهد شد:
http://www.tabatabaei.info/csharpsamples/Arrays%20in%20CSharp%20Ref%20New.GIF
پس دقت داشته باشيد كه در صورتيكه مي خواهيد طول يك آرايه را بيشتر كنين حتما ابتدا يك آرايه جديد ساخته و طول آن را بيشتر در نظر بگيريد و سپس مقادير آرايه قبلي را در آرايه جديد كپي كنين.
ساختار foreach در سي شارپ
شما مي توانيد با استفاده از ساختار foreach در سي شارپ اطلاعات موجود در آرايه هاي خود را خوانده و از آن ها استفاده كنين. در حقيقت foreach يك نوع حلقه است كه بر روي آرايه ها شما انجام مي شود و تعداد دفعات انجام آن برابر است با طول حلقه شما و در هر با حركت يكي از خانه هاي آرايه شما را در اختيارتان قرار مي دهد. از Syntax زير براي ايجاد حلقه foreach استفاده مي نمائيم:
int[] myNums = new int[4];
myNums[0] = 10;
myNums[1] = 320;
myNums[2] = 150;
myNums[3] = 510;
و براي نمايش اطلاعات داخل اين آرايه:
foreach(int num in myNums)
Console.WriteLine(num);
! دقت داشته باشين كه در ساختار foreach شما اجازه تغيير آرايه خود را به هر نحوي نداريد و اگر اين عمل را انجام دهيد به runtime error برخورد خواهيد كرد.
آرايه هاي مستطيلي در سي شارپ - Rectangular Arrays in CSharp
براي ايجاد يك آرايه مستطيلي از Syntax زير استفاده كنين:
int[,] myMatrix = new int[10,10];
با توجه به كد بالا شما داراي يك ماتريكس 10 در 10 هستيد و براي استفاده از هر خانه از اين آرايه به index هاي x , y آن احتياج دارين:
myMatrix[0,0] = 0;
myMatrix[0,1] = 100;
و ....
آرايه هاي نامنتظم - Jagged Arrays
اين آرايه ها را مي توان با استفاده از كد زير توليد كرد:
int[][] jaggedArray = new int[3][];
دقت داشته باشين كه در خط اول فقط طول يك بعد از آرايه را مشخص كرديم ==> 3
jaggedArray[0] = new int[3];
jaggedArray[1] = new int[2];
jaggedArray[2] = new int[4];
و در نتيجه اين خطوط شما يك آرايه به مانند تصوير زير خواهيد داشت:
http://www.tabatabaei.info/csharpsamples/Jagged%20Arrays%20In%20Csharp.GIF
استفاده از ArrayList در سي شارپ - ArrayList in Csharp
به خاطر ساختار آرايه ها در سي شارپ وقتي شما نياز به آرايه اي دارين كه طول آن نا مشخص است مي توانين از يك كلاس به نام ArrayList كه در namespaceي به نام System.Collection قرار دارد استفاده كنين.
ArrayList arList = new ArrayList();
arList.Add(10);
arList.Add(20);
arList.Add(40);
foreach(int i in arList)
Console.WriteLine(i);
ساختار ArrayList به نحوي است كه با شروع استفاده از آن يك طول مشخصي (مثلا 4) را براي خود اختصاص مي دهد. سپس وقتي شما 2 تا از اين خانه ها را با استفاده از متد Add پر كنين , طول آرايه داخلي خود را به دو برابر افزايش مي دهد و مقادير قبلي را داخل آرايه جديدش كپي ميكند و اين عمل را به صورت تصاعدي ادامه مي دهد كه در نتيجه اين عمل هم سرعت و كارايي خوبي دارد و هم طولش قابل تغيير است.
sunyboy
08-10-2009, 12:10 PM
در سي شارپ
از يك ديدگاه مي توان متغيير ها را در سي شارپ به دو دسته تقسيم كرد:
Class Variable
Instance Variable
تا اينجا ما چند تا مثال از نوع دوم داشتين. متغيير هايي كه بايد از طريق اشياءشان دسترسي داشت:
Person p = new Person();
p.Name = "Ali";
p.Age = 20;
همانطور كه مي بينين براي دسترسي به Name و Age بايد حتما از كلاس Person يك شيء بسازيم و از طريق شيء به متغيير ها دسترسي داشته باشيم.
اما فرض بفرمائيد كه روي مفهومي مثل انسان (همان كلاس Person) مي خواهيم "جمعيت" رو پياده كنيم. به نظر شما من مي تونم رو اين فرد ("Ali") مفهوم جمعيت رو پياده كنم؟ آيا اصلا اين منطقي است؟ مثل بگويم علي چند نفر است؟
به نظر مي رسد كه تعريف مفهوم (متغيير) جمعيت يا تعداد روي يك فرد غير منطقي باشد و اصولا جمعيت مربوط به كل انسان هاست نه فقط يك نفر!
براي همين ما بايد از نوع اول متغيير ها استفاده كنيم كه به آنها Class Variable مي گوييم براي ايجاد يك Class Variable بايد از كلمه Static در تعريف متغييرمان استفاده كنيم:
public class Person
{
public static int Count;
public string Name;
public int Age;}
حالا اگر بخواهم جمعيت رو مقدار دهي كنم يا اينكه مقدار جمعيت رو بخوانم بايد ابتدا نام كلاس و سپس نام متغييرم رو بنويسم:
Person.Count = 1000;
حالا همين شرايط رو براي متد ها نيز در نظر داشته بگيريد. فرض كنين كه من مي خواهم متدي داشته باشم كه جمعيت را برايم چاپ كند. آيا در تعريف اين متد بايد كلمه static به كار گرفته شود؟ مسلما بله! چون من مي خواهم رفتاري را نشان دهم كه مربوط به كل Concept ما يا همان كلاس ماست نه مربوط به يك شيء خاص. براي همين متد PrintCount رو به صورت زير تعريف مي كنم:
public static void PrintCount()
{
Console.WriteLine("Count:{0}",Count);
}
! فراموش نكنيد كه شما در متد هاي Static مجاز به استفاده از متغيير هاي غير static نمي باشيد.
وقتي مي خواهيم از يك متد Static استفاده كنيم كافيست كه اسم متد رو بعد از اسم كلاس بياوريم:
Person.PrintCount();
حالا با توجه به پست هايي كه تا امروز داشتيم, مي تونين 2 تا از متد هاي Staticي كه استفاده كرديم رو نام ببرين؟
كلاس هاي Static
مفهوم كلمه Static بر روي تعريف كلاس ها به اين معني است كه وقتي شما يك كلاس Static دارين تمامي Memberهاي اين كلاس بايد به صورت static تعريف شوند و اينكه شما نمي توانيد از اين كلاس objectبسازيد و البته اصولا احتياجي به اين كار هم ندارين.
sunyboy
08-10-2009, 12:12 PM
در پست دوازدهم در مورد اين موضوع صحبت كرديم كه در دات نت تمامي كلاس ها از يك كلاس به نام Object به ارث مي روند. در اين پست مي خواهيم كمي در مورد Inheritance بيشتر صحبت كنيم:
توارث در سي شارپ - Inheritance in CSharp
توارث يا به ارث بري همانطور كه از اسمش پيداست به اين معني است كه شما يه سري خواص و رفتار ها را از يك كلاس ديگر (كلاس پدر - Parent Class) به ارث ببرين و در نتيجه از همان امكانات و خصوصيات بدون نوشتن دوباره آن ها استفاده كنين. و در مواردي كه لازم مي دانين رفتار هاي آن ها را تغيير دهيد. اين اتفاقي است كه در دنياي واقعي نيز وجود دارد. به عنوان مثال شما احتمالا رنگ پوست , رنگ مو , رنگ چشم و شايد خصوصيات رفتاري و ... خود را از پدر و مادرتان به ارث ببرين. البته ممكن است كه شما رنگ مو خودتان را با استفاده از رنگ مو تغيير دهيد يا اينكه اخلاق و رفتارتان را با توجه به افكارتان به نسبت پدر يا مادرتان متفاوت باشد.
همانطور كه در مثال بالا هم ديديم Inheritance نيز به مانند بسياري از اصولا Object Oriented از دنياي واقعي الگو برداري شده است و كاملا قابل درك مي باشد.
فرض بفرمائيد كه من يك كلاس به نام Person يا همان انسان دارم. در اين كلاس خصوصيات "نام" و "سن" و همچنين يك متد به نام Print كه اطلاعات را براي من چاپ مي كند , تعريف شده اند. حالا يك كلاس به نام Emp يا كارمند ايجاد مي كنم. بعد از بررسي اين كلاس متوجه مي شوم كه كلاس Emp من داراي خصوصيات مشتركي با Person مي باشد و در نتيجه تصميم مي گيرم كه به جاي پياده سازي مجدد , از امكانات كلاس Person استفاده كنم.
حالا اجازه بدين روش پياده سازي Inheritance رو در سي شارپ بررسي كنيم.
public class Person
{
public string Name;
public int Age;
public void Print()
{
Console.WriteLine("Name:{0},Age:{1}", Name, Age);
}
}
حالا مي خواهم كلاس Emp رو از كلاس Person به ارث ببرم. براي اينكار كافيه كه بعد تايپ كردن نام كلاسم يك : قرار بدهم و بعد نام كلاسي كه مي خواهم از آن به ارث برم رو مشخص كنم:
public class Emp : Person
{
}
حالا اگر كدتون رو كامپايل كنين (با استفاده از Shift + Ctrl + B) و از كلاس Emp يك شيء در متد Main كلاس Programm ايجاد كنين خواهيد ديد كه كلاس شما داراي خواص Name و Age و متد Print مي باشد.
http://www.tabatabaei.info/csharpsamples/InheritanceSample.gif
اگر بخواهيم Class Diagram مثال فوق را رسم كنيم شكل زير پديد خواهد آمد:
http://www.tabatabaei.info/csharpsamples/INHERITANCE.GIF
حالا به جملاتي كه من مي پرسم با دقت پاسخ دهيد.
1. به نظر شما يك كارمند الزما يك انسان است؟
2. مي توان گفت كه هر انسان الزما يك كارمند است؟
جواب سوال اول مسلما "بله" خواهد بود. چرا كه وقتي يك كلاس (كارمند) از كلاس ديگر (انسان) به ارث مي رود با اطمينان مي توان گفت كه اشياء اين كلاس از جنس پدر نيز هستند.
جواب سوال دوم كاملا به شيء مورد نظر بستگي دارد و اصولا در سي شارپ اين كار به صورت عادي امكان پذير نيست.
اجازه بدين سوالات بالا رو به صورت سي شارپي ببينيم؟
Emp e = new Emp();
e.Name = "Ali";
e.Age = 22;
Person p = e; // اين كاملا در سي شارپ امكان پذير است
اما اگر شما اين كد را داشته باشيد , به Compile Time Error يا همان خطاهايي كه درزمان كامپايل ايجاد ميشوند بر خواهيد خورد:
Person p = new Person();
p.Name = "Ali";
p.Age = 44;
Emp e = p; // اين خط از كد خطا توليد مي كند
sunyboy
08-10-2009, 12:14 PM
صحبت هامون به اونجا رسيد كه گفتم شما مي تونين يك شيء از نوع فرزند رو به چشم يك شيء از جنس پدر ببينيد كه به اين عمل اصطلاحا "Upcast" گفته مي شود. يك مثال ساده هم ازش زديم و گفتيم كه اگر شما يك كارمند داشته باشين مي تونين اونو به چشم يك انسان نگاه كنين و در نتيجه از اطلاعاتي كه همه انسان ها دارند روي آن فرد خاص هم استفاده كنيم.
واقعيت اين است كه وقتي شما يك شيء از جنس فرزند دارين در حافظه Heap تمامي اطلاعات موجود به آن وجود دارند ولي وقتي شما به آن شيء با Reference پدر كار كنين فقط و فقط اطلاعاتي رو مي توانين استفاده كنين كه در پدر وجود دارند.
به شكل زير نگاه كنين. كلاس Person داراي "Name"و "Age" و متد "Print" مي باشد. و كلاس Emp كه از كلاس Person به ارث رفته يك فيلد به نام "Salary" نيز دارد.
http://www.tabatabaei.info/csharpsamples/INHERITANCE2.gif
حالا اگر من يك شيء از جنس Emp بسازم طبق اصولي كه گفتيم بايد تمامي متد ها و field هاي پدر + اطلاعات خودش را داشته باشد.
Emp e = new Emp();
e.Name = "Ali";
e.Age = 45;
e.Salary = 120000;
اما اگر من يك reference به شيء "e" از جنس Person ايجاد كنم فقط و فقط مي توانم اطلاعات مربوط به Person را استفاده كنم:
http://www.tabatabaei.info/csharpsamples/upcastsample.gif
در واقع رفتار Compiler سي شارپ به صورت شكل زير خواهد بود:
http://www.tabatabaei.info/csharpsamples/Upcastincsharp.GIF
البته شايد اين مثال كمي به نظرتون عجيب برسه! واقعيت اينه كه مثلا بالا شايد كمي غير واقعي باشه. اجازه بدين مثالمون رو اينجوري ادامه بديم.
فرض كنين كه شما يك متد دارين كه ورودي آن يك آرايه از اشياء از جنس Person مي باشد و بعد داخل اين متد اطلاعات شيء ها را يكي يكي چاپ مي كنيم:
public void PrintList(Person[] list)
{
foreach(Person p in list)
p.Print();
}
حالا شما مي خواهين اين متد را فراخواني كنين ولي شما 3 شيء از جنس كارمند دارين. آيا مي توانين يك آرايه از اشيايي با جنس كارمندان را به متد بالا پاس دهيد؟؟؟
Emp = e = new Emp("Ali",34,12000);
Emp e2 = new Emp("Reza",33,10000);
Emp e3 = "Saeid",28,20000);
// ساختن يك آرايه از Person با استفاده از اشيايي از جنس Emp
Person[] myList = new Person[]{e,e2,e3};
PrintList(myList);
پس همانطور كه مي بينيم عمل Upcast يكي از پر استفاده ترين اعمال در سي شارپ مي باشد.
! قبلا هم گفتيم كه تمامي كلاس ها در سي شارپ از يك كلاس خاص به نام object به ارث رفته اند. حالا اگر من يك آرايه بخواهم كه بتوانم داخلش هر نوع شيء اي رو قرار بدم كافيه كه يك آرايه از جنس object بسازم و بعد هر شيء كه دوست داشتم رو داخلش قرار بدم.
sunyboy
08-10-2009, 12:16 PM
در مورد Upcast توي پست قبلي صحبت كرديم. اما حالا مي خواهيم در مورد DownCast صحبت كنيم.
به كد زير دقت كنين:
Emp e = new Emp("Ali",24,120000);
Person p = e;
حالا فرض كنين كه من مجددا مي خواهم يك Reference از جنس Emp به شيء p داشته باشم. يعني اينكه مجددا "ديدم" رو از سطح بالا (Person) به سطح پائين تر (Emp) تغيير بدم. اينكار به صورت پيش فرض (implicit - غير صريح) امكان پذير نيست و در صورتيكه شما بخواهيد اين كار را انجام دهيد حتما بايد صراحتا (explicit) مسئوليت اين كار را به عهده بگيريد (casting).
اجازه بدين اين موضوع رو با يك مثال ساده بيشتر توضيح بدم.
يك نمايشگاه كه همه افراد اجازه بازديد دارند رو در نظر بگيرين. حالا داخل اين نمايشگاه شما به يكي از افراد اشاره مي كنين مي گين كه شما يك كارمند هستين!! آيا اين امكان پذير است؟ مسلما نه. مگر اينكه شما فرد مورد نظر رو بشناسين و بدانين كه ايشان يك كارمند هستند.
حالا اگر اين داستان رو در دنياي برنامه نويسي هم دنبال كنيم وقتي شما يك متد دارين كه ورودي آن اشيايي از جنس Person هستند. حالا شما مي خواهين يك Reference از جنس Emp به يكي از اين اشياء داشته باشين. اين كار امكان پذير نيست مگر اينكه شما مطمئن باشيد كه اين فرد يك كارمند بوده و در هنگاه اجراي اين متد با استفاده از Upcast وارد اين متد شده است.
Casting
حالا اگر شما مطمئن بوديد كه يك فرد واقعا يك كارمند است و خواستين كه عمل Downcast را انجام دهيد بايد مسئوليت اينكار را بعهده بگيريد. براي اينكار كافيست به صورت زير عمل كنين:
Emp e2 = (Emp) p;
همانطور كه مي بينين عمل Casting يا همان مسئوليت پذيري با استفاده از دو پرانتز و بعد جنس مورد نظر صراحتا اعلام شده است (explicit).
شكل حافظه زير داستان را كاملا شفاف خواهد كرد:
http://www.tabatabaei.info/csharpsamples/Downcast%20in%20CSharp.GIF
همانطور كه در تصوير مي بينين در DownCast در حقيقت شما "ديدتان را گسترش" مي دهيد و اين كار هيچ تغييري در ماهيت شيء شما ايجاد نمي كند. حالا اگر شما با استفاده از Casting مسئوليت يك DownCast غير متعبر را به عهده بگيرين با يك خطاي runtime برخورد خواهيد كرد:
Person p = new Person("Reza", 45);
Emp e = (Emp) p;
در شكل حافظه زير مشخص است كه عمل DownCast با شكست مواجه خواهد شد. زير اطلاعات مورد نياز در شيء p براي تبديل شدن به Emp وجود ندارد.
http://www.tabatabaei.info/csharpsamples/Invalid%20Downcast%20in%20CSharp.GIF
sunyboy
08-10-2009, 12:17 PM
Access Modifiers in CSharp - كنترل سطوح دسترسي در سي شارپ*
* خيلي ممنون مي شم اگر كسي بتونه توي تصحيح اين عبارت ها به فارسي روان تر كمكم كنه.
همانطور كه تا اينجاي كار ديدين ما از دو عبارت public و private در كد هايي كه نوشتيم استفاده كرده بوديم. كه در حقيقت كنترل كننده سطح قابل دسترسي يك متغيير يا متد يا كلاس و يا ... مي باشد. اگر اشتباه نكنم قبلا هم گفتم وقتي متغييري public تعريف مي شود از هر جايي قابل دسترسي است. حالا اجازه بدين اين موضوع رو كمي بيشتر توضيح بدهيم.
اصولا كاربرد Access Modifier ها بر روي دو حوزه (تا اينجايي كه ما اطلاعات داريم) مي باشد.
1. در تعريف كلاس يا Enum يا Structure ها.
2. در تعريف متغيير ها , Method ها , Constructor ها و ...
كلا ما ۵ سطح دسترسي داريم:
public
protected
internal
protected internal يا internal protected
private
public access modifier: به اين معني كه هيچ گونه محدوديتي قائل نيستيم. امكان استفاده از آن براي كلاس ها (آيتم هاي اول) و متغيير ها و ... (آيتم هاي دوم) وجود دارد. وقتي من كلاس public تعريف مي كنم به اين معناست كه هر كسي (چه داخل پروژه من و چه خارج از پروژه من) امكان استفاده از كلاس من را دارد.
وقتي متغيير يا متدي به صورت public تعريف مي شود هر كسي كه به كلاس دسترسي دارد مي تواند از متغيير يا متد شما استفاده كند.
!! دقت داشته باشين كه شما اجازه ندارين به متغيير ها و ... درون كلاس دسترسي بالاتر از خود كلاس بدهيد.
protected access modifier: وقتي يك متغيير يا متد يا ... به صورت protected تعريف مي شوند. داخل كلاس يا ... كه تعريف شده و كلاس هايي كه از آن كلاس به ارث رفته اند قابل دسترسي مي باشد.
به عنوان مثال كلاس Person داراي يك متد به نام GetInfo() است كه به صورت زير تعريف شده است:
public class Person
{
public int Age;
public string Name;
protected void GetInfo()
{
Console.WriteLine("Name:");
this.Name = Console.ReadLine();
Console.WriteLine("Age:");
this.Age = int.Parse(Console.ReadLine());
}
}
حالا كلاس Emp كه از كلاس Person به ارث رفته است مي خواهد از متد GetInfo() استفاده كند و چون متد GetInfo() به صورت protected تعريف شده و در نتيجه تمامي كلاس هايي كه از Person به ارث بروند امكان استفاده از GetInfo() را دارند, قادر به انجام اين كار است.
در صورتيكه از هيچ كجاي ديگر امكان استفاده از اين متد براي كلاس هاي ديگر مثل كلاس Programm وجود ندارد!!
http://www.tabatabaei.info/csharpsamples/ProtectedAccessModifier.GIF
! دقت داشته باشين كه protected براي گروه آيتم هاي اول (كلاس ها و...) قابل استفاده نيست.
internal access modifier:خيلي از مواقع پيش مي آيد كه شما كلاسي را ايجاد مي كنين كه احتمال دارد در پروژه هاي ديگري بيرون از اين پروژه جاري استفاده شود. (مثلا در مورد برنامه نويسي چندلايه كه اميدوارم در آينده در موردش بيشتر توضيح بدهيم). حالا فرض فرمائين كه شما نمي خواهين يك كلاس يا متغيير يا ... آن كلاس در اختيار كساني قرار بگيرد كه بيرون از پروژه جاري شما از اين Assembly استفاده مي كنند. (مثلا شما يك Component رو در نظر بگيرين كه قرار است داخل n تا پروژه ديگر استفاده شود). براي همين مي توانيد با استفاده از internal فقط به كلاس هايي كه داخل اين پروژه شما هستند اجازه دهين كه از اين كلاس يا متغيير يا ... استفاده كنند.
internal protected access modifier: دسترسي فوق تلفيقي است از internal و protected به اين معنا كه اگر متغييري به صورت internal protected تعريف شده باشد. كلاس هايي كه داخل اين پروژه هستند و يا از كلاسي كه اين متغيير داخلش قرار دارد به ارث رفته باشند , اجازه دارند كه از اين متغيير استفاده نمايند.
! دقت داشته باشين كه protected internal هم براي گروه آيتم هاي اول (كلاس ها و...) قابل استفاده نيست.
private access modfier: متغيير و يا متد و ... كه به صورت private تعريف شود , فقط و فقط داخل همان كلاس قابل استفاده خواهد بود.
! دقت داشته باشين كه private براي گروه آيتم هاي اول (كلاس ها و...) قابل استفاده نيست.
sunyboy
10-10-2009, 10:44 PM
خصوصيات در سي شارپ - Properties in CSharp
يكي از مطالبي كه جا مونده بود بحث Properties ها در سي شارپ است. در تعريف Properties مي توان گفت كه Properties يك يا دو متد است كه با يك field private كار مي كند.
براي تعريف Properties ها از Synatx زير استفاده مي كنيم:
public string Name
{
get{return _Name;}
set{_Name = value;}
}
private string _Name;
همانطور كه در مثال بالا مشاهده مي كنين. ما يك فيلد private به نام Name_ تعريف كرديم كه در Property اي به نام Name مورد استفاده قرار گرفته است. در حقيقت Property Name از Name_ براي ذخيره مقدار و بازيابي آن استفاده مي كنند.
اما اگر Property ها از فيلد ها براي نگهداري و بازيابي اطلاعاتشان استفاده مي كنند چه دليلي دارد كه ما از property ها استفاده كنيم؟
در پاسخ به اين سوال بايد گفت كه به دو دليل از Property ها استفاده مي شود:
1. كنترل و مديريت اطلاعات در حين مقدار دهي و خواندن مقادير
در توضيح اين مورد بايد بگم كه اگر شما يك فيلد براي سن در نظر بگيريد و به جنس آن را از نوع عددي مثلا intتعيين كنين برنامه نويساني كه از كلاس شما استفاده مي كنند (Class Programmer ها) مي توانند مقاديري بين دو عدد و براي سن مشخص كنند. اما در واقعيت اين اعداد براي سن كاملا غير معتبر مي باشد. پس با اعمال كنترل هاي لازم در قسمت set براي Property مي توان بازه اي كه براي اعداد مشخص شده است را تعيين كرد.
private int _Age;
public int Age
{
get{return _Age;}
set{ if(!(value >= 0 && value <= 100))
_age = 10;
else
_age = value;
}
}
در اين مثال در صورتيكه سن بين 0 تا 100 نباشد 10 در نظر گرفته مي شود.
2. امكان تعيين سطح دسترسي براي فيلد ها
نكته بعدي ايجاد Property هاي است كه فقط خواندني يا فقط نوشتني هستند. با استفاده از اين روش مي توانين اطلاعات را محدود به خواندن يا نوشتن نمائيد تا برنامه نويسان بر اساس نيازشان فقط اطلاعات را بخوانند يا بنويسند. البته شما مي توانين Property هايي كه هم خواندني و هم نوشتني هستند داشته باشين.
private int _Count;
public int Count
{
get{return _Count;}
}
در اين مثال تعداد يك Property فقط خواندني مي باشد.
! نكته اي كه مي توان در سي شارپ 2.0 از آن استفاده كرد اينست كه شما مي توانين براي
Property ها دو سطح دسترسي مختلف تعيين كنيد :
public string Name
{
get{return _Name;}
protected set{_Name = value;}
}
مانطور كه مي بينين Property Name به صورت public تعريف شده و در نتيجه همه مي توانند از آن اطلاعات بخوانند ولي براي مقدار دهي آن با توجه به protected بودن آن فقط افرادي اين امكان را دارند كه از اين كلاس به ارث رفته باشند. دقت كنين كه شما اجازه دارين دسترسي ها بر روي متد هاي get و set كمتر نمائيد و حق بيشتر كردن دسترسي را نداريد.
sunyboy
10-10-2009, 10:48 PM
Overriding in CSharp
همانطور که قبلا گفتم تمامی کلاس ها در سی شارپ چه بخواهند و چه نخواهند از کلاسی به نام object به ارث می روند و در نتیجه خصوصیات این کلاس به آن ها ارث می رسد. به عنوان مثال متد ToString که در تمامی کلاس هایی که ما ایجاد می کنیم وجود دارد و وقتی روی یک شیء متد ToString را فراخوانی می کنیم یک String از آن شیء در اختیار ما قرار می دهد که به صورت پیش فرض این رشته نام کامل کلاس شامل Namespace.Class می باشد.
Person p = new Person();
p.Name = "Ali";
p.Age = 20;
Console.WriteLine(p);
// ConsoleApplication13.Person
در مثال بالا من یک شیء از کلاس Person ایجاد کرده و بعد از ست کردن نام و سن دستور چاپ آن شیء را از طریق متد WriteLine ارسال کرده ام. با توجه به اینکه متد WriteLine در موقع چاپ اشیاء به یک String نیاز دارد متد ToString را بر روی شیء من فراخوانی خواهد کرد. در نتیجه یک رشته از شیء من چاپ خواهد شد که نام کامل کلاس یعنی : ConsoleApplication13.Person می باشد.
به شکل زیر دقت کنین:
http://www.tabatabaei.info/csharpsamples/OverridingInCsharp1.gif
همانطور که می بینین متد ToString از کلاس پدر که کلاس object است به من به ارث رسیده است. اما رفتار این متد رفتاری نیست که من نیاز داشته باشم به این معنا که من نیاز دارم وقتی متد ToString را روی اشیایی از جنس کلاس Person (که من ایجاد کردم) فراخوانی می شود به جای نام کامل کلاس (Qualified Name) اطلاعات آن را (یعنی نام و سن) در اختیار من قرار دهد. به این معنی که می خواهم رفتارهای کلاس پدر را تغییر دهم که اصطلاحا به این کار Overriding می گویند.
برای اینکه من بتوانم رفتار کلاس پدر را تغییر دهم باید متد مورد نظرم را override کنم که این کار با استفاده از عبارت override و نوشتن مجدد متد با رفتار مورد نظر خودمون امکان پذیر می باشد:
public class Person
{
public string Name;
public int Age;
override public string ToString()
{
return string.Format("Name:{0}, Age:{1}",Name,Age);
}
}
بعد از نوشتن این تکه کد اگر دوباره کدی که در ابتدا نوشتیم را اجرا کنیم با رفتار جدید متد ToString که در واقع چاپ نام و سن می باشد مواجه خواهید شد.
Virtual Methods in CSharp
اصولا وقتی یک کلاس ایجاد می کنیم باید در نظر داشته باشیم که اگر این کلاس توسط کلاس دیگری به ارث رفت کدامیک از متد ها یا Property های آن توسط فرد دیگری استفاده خواهند شد و اگر کسی در کلاس جدید نیاز به تغییر رفتار داشت این امکان را در اختیار وی قرار دهیم.
فرض بفرمائید که من یک کلاس به نام Person با دو Propertyی نام و سن و یک Method به نام Print که نام و سن را چاپ می کند ایجاد کرده ام. حالا می خواهم کاری کنم که کلاس هایی که از کلاس Person به ارث می روند بتوانند رفتار متد Print را override کنند. برای اینکه این امکان را در اختیار فرزندانم (کلاس هایی که از من به ارث می روند) قرار دهم باید در تعریف متد از عبارت virtual استفاده کنم. به کد کلاس Person دقت کنین:
public class Person
{
public string Name;
public int Age;
public virtual void Print()
{
Console.WriteLine("Name: {0}, Age: {1}", Name, Age);
}
}
حالا کلاس Emp را از کلاس Person به ارث می برم و یک فیلد جدید به نام Salary به آن اضافه می کنم و انتظار دارم که با override کردن متد Print کاری کنم که وقتی Print روی اشیایی از جنس Emp فراخوانی می شوند علاوه بر نام و سن , حقوق را نیز چاپ کند.
public class Emp : Person
{
public decimal Salary;
override public void Print()
{
Console.WriteLine("Name:{0}, Age: {1}, Salary: {2}",Name,Age,Salary);
}
}
با توجه به کد بالا در صورتیکه که این کد را برای تست بنویسم باید علاوه بر نام و سن , حقوق کارمند را هم چاپ نماید:
Emp e = new Emp();
e.Name = "Reza";
e.Age = 25;
e.Salary = 240000;
e.Print();
!! نکته بسیار مهم در مورد Overriding این است که در صورتیکه Reference شما به یک شیء از جنس پدر نیز باشد , کامپایلر سی شارپ بدون توجه به نوع Reference به ماهیت شیء توجه کرده و متد مربوطه را چاپ می نمایند. به کد زیر دقت کنین:
Emp e = new Emp();
e.Name = "Saeid";
e.Age = 44;
e.Salary = 54000;
// در این خط از کد عملیات UpCast به سادگی انجام می شود
Person p = e;
// فراخوانی متدی که در کلاس پدر وجود داشته و در کلاس فرزند override شده است
p.Print();
با اینکه reference ما به شیء از جنس پدر(Person) می باشد ولی به دلیل override شدن در کلاس فرزند , پیاده سازی متد فرزند یعنی چاپ نام , سن و حقوق اجرا می شود.
sunyboy
10-10-2009, 10:49 PM
چند ريختي در سي شارپ
بررسي مفهوم overriding را با يك مثال پيگيري مي كنيم. يك سازمان يا شركت را در نظر بگيريد. اين شركت داراي دو نوع كارمند مي باشد. نوع اول كارمندان حقوق بگيري هستند كه حقوقشان را به صورت ماهيانه و با توجه به پايه حقوقي ثابتي كه برايشان در نظر گرفته شده است دريافت مي كنند. به عنوان مثال "علي رضايي" يك كارمند حقوق بگير است كه براي هر ماه مبلغ "100,000" دريافت مي كند.
نوع دوم كارمنداني هستند كه به صورت ساعتي حقوقشان را دريافت مي كنند و براي هر ساعت كار يك مبلغ مشخصي دريافت مي كنند. به عنوان مثال "رامين احمدي" يك كارمند ساعتي است كه براي هر ساعت كار مبلغ "3000" دريافت مي كند.
در اين مثال باتوجه به اينكه تمامي كارمندان داراي اطلاعات مشتركي هستند (مثل نام و شماره كارمندي و اطلاعات سوابق و ....) تصميم گرفتيم يك كلاس پايه به نام Emp كه مخفف Employee است در نظر بگيريم و اطلاعات مشترك را در اين كلاس تعريف كنيم:
http://www.tabatabaei.info/csharpsamples/Overriding%20%284%29.GIF
همانطور كه مي بينين با توجه به اينكه مفهوم حقوق براي كلاس كارمند (بدون مشخص بودن نوعش) يك مفهوم انتزاعي است من در اين مثال حقوق (يا همان Salary) را به صورت virtual تعريف كرده ام , تا كلاس هايي كه از كلاس Emp به ارث مي روند با override كردن اين Property پياده سازي درست آن را در نوع خود انجام دهند.
پس دو كلاس MonthlyEmp و HourlyEmp را كه از كلاس پايه Emp به ارث رفته اند به صورت زير تعريف خواهند شد:
http://www.tabatabaei.info/csharpsamples/Overriding%20%281%29.GIF
در كلاس MonthlyEmp (كه در واقع كارمند حقوق بگير ماهيانه مي باشد) حقوق بر اساس "پايه حقوق" محاسبه مي شود.
http://www.tabatabaei.info/csharpsamples/Overriding.gif
همانطور كه در كد مي بينيد , در كلاس HourlyEmp (كه همان كارمند ساعتي است) حقوق براساس "مبلغ پايه هر ساعت" * "تعداد ساعات كاركرد" محاسبه و پرداخت و خواهد شد.
مثال را بايد ايجاد يك كلاس چهارم به نام Company تكميل مي كنيم. در اين كلاس يك آرايه از كارمند (Emp) داريم. دليل اينكار كاملا آشكاراست. چون احتمال ايجاد كلاس هاي جديد (در واقع نوع هاي كارمندان جديد) وجود دارد در نتيجه من يك آرايه از كلاس پدر كه همان Emp است براي نگهداري ليست كارمندان ايجاد مي كنم.
http://www.tabatabaei.info/csharpsamples/Overriding%20%282%29.GIF
همانطور كه در كد مي بينين يك متد به نام PaySalary در اين كلاس ايجاد شده كه در واقع هر ماه يكبار توسط مدير عامل شركت جهت پرداخت حقوق تمامي كارمند استفاده مي شود. صرف نظر از اينكه در موقع فراخواني واقعا چه نوع كارمندي در اين آرايه پر شده است , انتظار من اين است كه اگر كارمند ساعتي بود از روش محاسبه كارمند ساعتي و اگر كارمند حقوق بگير بود از روش محاسبه كارمند حقوق بگير حقوق افراد پرداخت شود. اين دقيقا همان نكته ايست كه در فقط در مواقعي كه شما از overridingاستفاده كنين اتفاق خواهد افتاد. به عبارت ديگر "در overriding صرف نظر از نوع ديدگاه ما (reference) به يك شيء , ماهيت آن مشخص كننده فراخواني متد پدر يا فرزند خواهد بود" يعني اگر حتي reference ما به يك شيء MonthlyEmp از نوع Emp باشد (يعني عمل upcase اعمال شده باشد) باز در مواقع فرخواني متد , پياده سازي فرزند مورد استفاده قرار خواهد گرفت. اصولا اين عمل را در دنياي برنامه نويسي شيءگرا "چند ريختي" يا Polymorphysm مي گويند.
http://www.tabatabaei.info/csharpsamples/Overriding%20%283%29.GIF
براي دريافت مثال كامل اين پست مي توانين از اين لينك استفاده كنين:
بارگزاري مثال (http://www.tabatabaei.info/csharpsamples/OverridingSample.rar)
sunyboy
10-10-2009, 10:50 PM
Method Hiding in CSharp
پس بررسي مفهوم overriding خوب است كه كمي در رابطه با مفهوم Hiding هم صحبت كنيم. در واقعا Hiding دوباره نويسي يك متد است كه قبلا در كلاس پدر نوشته شده. اما نكته اي كه وجود دارد اين است كه در Hiding كامپايلر سي شارپ با توجه به نوع Reference شما متد را اجرا مي كند.
اگر مثال آخرين پست را بررسي كنيم متوجه مي شويم كه در آن مثال ما يك ليستي داشتيم از Emp ها (يك آرايه از Emp) كه در حقيقت اشيايي از MonthlyEmp و HourlyEmp را داخلشان قرار مي داديم. در واقع ما اشيايي از جنس كلاس هاي فزرند داشتيم اما به Referenceهايي از جنس كلاس هاي پدر. نكته حائز اهميت اين است كه وقتي روي اين اشياء متد ShowInfo را فراخواني مي كرديم. با اينكه ديد (Reference) ما به اشياء از نوع Emp بود ولي Salary را باتوجه به ماهيت اشياء (چيزي كه با آن newشده بودند)فراخواني مي كرد و به نوع Reference ما اهميت نمي داد. با استفاده از همين خاصيت ما Polymorphism را در مثال قبل پياده سازي كرديم.
Hiding در واقع نقطه مقابل Overriding است. به اين ترتيب كه شما بدون توجه به ماهيت شيء (چيزي كه new شده است)و صرفا با توجه به نوع Reference پياده سازي مربوطه را فرخواني مي كنين.
در مثال زير من يك كلاس به نام Person دارم كه متدي به نام Print را پياده سازي كرده است.
يك كلاس ديگر به نام Student از كلاس Person به ارث رفته و باز هم متد Print را پياده سازي كرده است. دقت بفرمائيد كه در Hiding هنگام پياده سازي مجدد از كلمه كليدي new استفاده خواهيم كرد.
http://www.tabatabaei.info/csharpsamples/HidingSample1.GIF
پس در صورتيكه من يك شيء از جنس Student بسازم ولي ديدم را به Person محدود كنم. وقتي متد Print را فراخواني مي كنم پياده سازي كه در كلاس Person وجود دارد فراخواني مي شود. همانطور كه گفتيم در Hiding كامپايلر با توجه به Reference ما متد مورد نظر را فراخواني مي كند و به ماهيت شيء توجهي نمي كند.
http://www.tabatabaei.info/csharpsamples/HidingSample.GIF
بارگزاري مثال Hiding (http://www.tabatabaei.info/csharpsamples/HidingSample.rar)
sunyboy
10-10-2009, 10:53 PM
فراخواني سازنده ها - Constructor Calling
وقتي از يك كلاس كه يك كلاس ديگر به ارث رفته است , شيء مي سازيم در واقع متد سازنده آن كلاس و تمامي كلاس هايي كه به عنوان پدر اين كلاس مطرح هستند را نيز فراخواني مي نمائيم. به عنوان مثال كلاس Customer از كلاس Person به ارث رفته است. در كلاس Person من دو نوع Constructor دارم. يكي همان Default Constructor است كه به صورت public و بدون پارامتر تعريف شده و ديگر داراي دو پارامتر است. يكي از جنس String كه نام فرد است و ديگري از جنس int كه سن فرد مي باشد:
public class Person
{
public int Age;
public string Name;
public Person()
{
Console.WriteLine("Default Constructor of Person Called");
}
public Person(string Name, int Age)
{
this.Name = Name;
this.Age = Age;
Console.WriteLine("2nd Constructor of Person Called");
}
public virtual void Print()
{
Console.WriteLine("Name: {0}, Age: {1}", Name, Age);
}
}
همانطور كه مي بينيد من براي اينكه مشخص بشه كه از كدام Cosntructor استفاده مي شود در هر دو Constructor يك جمله چاپ مي كنم.
حالا كلاس Customer را از كلاس Person به ارث مي بريم:
public class Customer : Person
{
public decimal Credit;
override public void Print()
{
Console.WriteLine)"Name: {0}, Age: {1}, Credit: {2}", Name, Age, Credit);
}
}
حالا براي تست يك شيء از كلاس Customer ايجاد مي كنم:
Customer C = new Customer();
c.Name = "Ali";
c.Age = 20;
c.Credit = 2000;
c.Print();
كه در نتيجه در محيط Console خروجي شبيه به اين خواهيم داشت:
http://www.tabatabaei.info/csharpsamples/ConstructorCalling01.GIF
همانطور كه در خروجي هم مشخص شده است , با اينكه من Constructor كلاس فرزند را فراخوني كردم اما Default Constructor كلاس پدر نيز فراخواني شده است.
نكته اي كه وجود دارد اين است كه وقتي شما مشخص نكنين كه كدام Constructor از كلاس پدر فرخواني شود سازنده پيش فرض كلاس پدر فراخواني خواهد شد.
اما در صورتيكه نخواهيم سازنده پيش فرض فراخواني شود بايد چه كنيم؟ يا اگر در كلاس پدر سازنده پيش فرض نداشتيم چطور؟
در صورتيكه شما مي خواهيد يكي از سازنده هاي پدر را صراحا خودتان اعلام كنين كافي است كه در مقابل تعريف سازنده خود از كلمه base استفاده كنين:
public Customer(string Name, int Age, decimal Credit): base(Name,Age)
{
this.Credit = Credit;
Console.WriteLine("Customer Constructor called");
}
حالا اگر مجددا يك شيء از كلاس Customer ايجاد كنين , نتيجه اي متفاوت خواهيد داشت:
http://www.tabatabaei.info/csharpsamples/ConstructorCalling02.GIF
همانطور كه در تصوير خروجي هم مشخص است. ابتدا سازنده پدر فرخواني شده (كه البته با اين روش من كد كمتري هم نوشته ام و از كدي كه سازنده پدر وجود دارد استفاده مجدد كرده ام) و بعد سازنده كلاس Customer.
دانلود مثال اين پست (http://www.tabatabaei.info/csharpsamples/ConstructorCalling.rar)
sunyboy
10-10-2009, 10:55 PM
بازنويسي عملگرها در سي شارپ - Operator Overloading in csharp
تمامي عملگرها (operators) در سي شارپ داراي رفتار هاي از پيش تعيين شده اي هستند و شما مي توانيد از اين عملگرها در عبارت هاي خود استفاده کنين:
int i = 10; int j = 20; int m = i * j / 2 + 14; Console.WriteLine("m is :{0}",m);
اما اگر شما عبارت زير را بنويسيد چطور؟
Person p = new Person("Ali",20); Person p2 = new Person("Reza",30); Person p3 = p + p2; Console.WriteLine("Name: {0}, Age:{1}",p3.Name , p3.Age);
در صورتيکه اين کد را Compile کنين متوجه يک خطاي Compile Time خواهيد شد که به شما توضيح مي دهد که امکان جمع بستن دو Person با يکديگر وجود ندارد يا به عبارت ديگر عملگر + براي Person تعريف نشده است.
در سي شارپ شما مي توانين بسياري از عملگرها را دوباره نويسي کنين به عبارت ديگر شما مي توانين در مواقع لزوم تعريف جديد از يک عملگر در سي شارپ داشته باشيم. در تصوير زير ليست عملگرها به همراه توضيحاتي راجع به امکان بازنويسي شان مي بينيد.
http://www.tabatabaei.info/csharpsamples/CSharpOperators.gif
همانطور که در تصوير مشخص است شما نمي توانين تمام عملگرها را دوباره نويسي کنين.
در سي شارپ يک کلمه کليدي به نام operator وجود دارد که براي بازنويسي عملگرها بايد از آن استفاده کنيم. به عنوان مثال براي اينکه مثالي که در ابتدا نوشتيم درست عمل کند و وقتي عبارت بالا را اجرا مي کنيم يک شيء جديد از جنس Person ايجاد شود که نامش از جمع بستن نام اين دو فرد و سنش از جمع بستن سن اين دو فرد تشکيل شود من در کلاس Person اين کد را مي نويسم:
public static Person operator +(Person p1, Person p2) { Person p = new Person(); p.Name = p1.Name + " " + p2.Name; p.Age = p1.Age + p2.Age; return p; }
دقت بفرمائيد که حاصل جمع دو شيء از جنس Person يک Person مي باشد و من در پياده سازي عملگر + يک فرد جديد ساخته ام. اما اگر بخواهيم عملگر == يا همان برابري را دوباره نويسي کنيم بايد دقت کنيم که خروجي آن بايد يک عبارت true /false و از جنس bool باشد. در نتيجه اگر من بخواهم که مبناي مقايسه دو شيء از جنس Person را براساس نامشان قرار دهم از اين کد استفاده مي کنم:
public static bool operator ==(Person p1, Person p2) { return p1.Name == p2.Name; }
نکته مهم در اين مثال اين است که شما وقتي عملگر == (برابري) را دوباره نويسي مي کنين بايد عملگر != (نابرابري) را هم دوباره نويسي کنين:
public static bool operator !=(Person p1, Person p2) { return !(p1==p2); }
بارگزاري مثال (http://www.tabatabaei.info/csharpsamples/Operatoroverloading.rar)
sunyboy
10-10-2009, 10:56 PM
مثال برای overload کردن عملگرها
با اعداد کامپلکس که آشنا هستید ، a+bi که a, b عدد حقیقی هستند و i= جذر -1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Numbers
{
public class ComplexNumber
{
double _Real = 0;
double _Image = 0;
public double Image
{
get { return _Real; }
set { _Real = value; }
}
public double Real
{
get { return _Image; }
set { _Image = value; }
}
public ComplexNumber()
{ }
public ComplexNumber(double RealPart, double ImagePart)
{
Real = RealPart;
Image = ImagePart;
}
public ComplexNumber(int RealPart, int ImagePart)
{
Real = (double)RealPart;
Image = (double)ImagePart;
}
public override string ToString()
{
return string.Format("{0:G} + {1:G}i", _Real, _Image);
}
public static ComplexNumber operator +(ComplexNumber A, ComplexNumber B)
{
ComplexNumber c = new ComplexNumber();
c.Real = A.Real + B.Real;
c.Image = A.Image + B.Image;
return c;
}
public static ComplexNumber operator -(ComplexNumber A, ComplexNumber B)
{
ComplexNumber c = new ComplexNumber();
c.Real = A.Real - B.Real;
c.Image = A.Image - B.Image;
return c;
}
public static ComplexNumber operator *(ComplexNumber A, ComplexNumber B)
{
ComplexNumber c = new ComplexNumber();
c.Real = (A.Real * B.Real) - (A.Image * B.Image);
c.Image = (A.Image * B.Real) + (A.Real * B.Image);
return c;
}
public static ComplexNumber operator /(ComplexNumber A, ComplexNumber B)
{
ComplexNumber c = new ComplexNumber();
double s;
s = B.Real * B.Real + B.Image * B.Image;
c.Real = ((A.Real * B.Real) + (A.Image * B.Image)) / s;
c.Image = ((A.Image * B.Real) - (A.Real * B.Image)) / s;
return c;
}
}
}
و برای استفاده از این کلاس
ComplexNumber c1 = new ComplexNumber(1, 1);
ComplexNumber c2 = new ComplexNumber(4d, 9d);
MessageBox.Show((c1 + c2).ToString());
MessageBox.Show((c1 - c2).ToString());
MessageBox.Show((c1 * c2).ToString());
MessageBox.Show((c1 / c2).ToString());
sunyboy
10-10-2009, 10:57 PM
كنترل خطاها در سي شارپ - Exception Handling in CSharp
در تمامي زبان هاي برنامه نويسي روش هايي براي مقابله با خطا ها وجود دارد. عموما خطا ها را از ديد زمان وقوع به دو دسته Compile Time Errors و RunTime Error ها تقسيم كرد. خطا هاي گروه اول يا همان خطاهاي زمان كامپايل توسط Compiler تشخيص داده و به كاربر نمايش داده مي شوند. خطاهايي از قبيل استفاده از يك متغييري كه مقدار دهي نشده است يا اشتباه در Syntax و ....
خطاهاي زمان اجرا عموما خطاهايي هستند كه در زمان كامپايل توسط Compiler تشخيص داده نشده اند و در زمان اجراي نرم افزار بروز مي كنند. خطاهایی مثل مشکل در اتصال به بانک اطلاعاتی , ورود اطلاعات اشتباه توسط کاربر , عدم دسترسی به فایل مورد نظر و ....
اصولا هر برنامه نویس در هنگام نوشتن خطوط کد خود می تواند احتمال وقوع خطا را پیش بینی کند مثلا در مثال زیر من از کاربر انتظار دارم تا یک عدد را برای من تایپ کند:
http://www.tabatabaei.info/csharpsamples/Exception1.gif
اما می توانم حدس بزنم که کاربر می تواند به جای 10 کلمه "ALI" را سهوا یا عمدا تایپ نماید. که در این صورت نرم افزار من دچار اشکال شده و از برنامه خارج خواهد شد.
در سی شارپ برای اینکه بتوانیم خطا ها را کنترل کنیم , خطوطی را که احتمال وقوع خطا در آن ها زیاد است را در try catch می نویسیم.
برای اینکار کافی است که به صورت زیر عمل کنیم:
http://www.tabatabaei.info/csharpsamples/Exception2.gif
همانطور که می بینید من خطوطی از کد که احتمال خطا دارد را داخل بلاک try قرار دادم و عکس العمل خودم در موقع بروز خطا را نیز در بلاک catch. در واقع در صورتیکه در هر یک از خطوط داخل block try دچار خطا شویم به قسمت catch ارجاع داده خواهیم شد و می توانیم آنجا عکس العمل لازمه را نشان دهیم.
در سی شارپ و در namespace ی به نام System یک کلاس به نام Exception وجود دارد که در حقیقت base classی برای تمام انواع خطا ها در سی شارپ می باشد. به این معنا که تمامی خطاهایی که در سی شارپ وجود دارند از Exception به ارث رفته اند و در نتیجه تمامی آنها به نوع Exception می باشند. برای اینکه بتونین لیست Exception ها را ببینین کافیه که از منوی Debug گزینه Exception را کلیک کنین تا لیست Exception ها را به تفکیک namespace ملاحظه بفرمائید. (می تونین از Alt + Ctrl + E به عنوان Shortcut استفاده کنین).
http://www.tabatabaei.info/csharpsamples/Exception3.gif
با توجه به مثال اولی که نوشتیم تا اینجا ما توانستیم در مواقعی که احتمال وقوع خطا وجود دارد با استفاده از try و Catch مانع از بسته شدن نرم افزارمان یا به اصطلاح crash شدن آن شویم. مرحله بعدی تشخیص دادن نوع خطا و در نهایت نشان دادن عکس العمل مناسب در مقابل خطای مورد نظر است.
تولید خطا در سی شارپ
اما قبلا از اینکه به این موارد بپردازیم اجازه بدین بررسی کنیم که در سی شارپ چطور می توانیم تولید خطا کنیم؟
برای ایجاد یک خطا در زمان runtime در سی شارپ کافی است که یک شیء از جنس Exception را بوسیله کلمه کلیدی throw پرتاب کنیم. به مثال زیر دقت کنین:
http://www.tabatabaei.info/csharpsamples/Exception4.gif
در نتیجه کد بالا که در واقع تولید یک خطا را نمایش می دهد خطای زیر از نرم افزار ایجاد شده و نرم افزار بسته خواهد شد:
http://www.tabatabaei.info/csharpsamples/Exception5.gif
در واقع وقتی شما با یک خطا برخورد می کنید به این معنی است که یک شیء از جنس Exception یا کلاس هایی که از Exception به ارث رفته اند توسط کلمه کلیدی throw پرتاب شده است. حالا اگر شما از block های try , catch استفاده کنین می توانین در مقابل آن خطا عکس العمل نشان دهید:
http://www.tabatabaei.info/csharpsamples/Exception6.gif
در تصویر بالا من هنگام پرتاب کردن خطا یک متن را به عنوان توضیح خطا در سازنده کلاس Exception قرار داده ام که این متن را بعد ها از طریق متغییر Message می توانم به دست بیاورم. اما نکته ای که وجود دارد این است که برای اینکه بتوانید متن خطا و محل آن را به دست بیاورید به آن شیء ای که پرتاب شده است نیاز دارید. پس من با این یک متغییر به آن شیء دسترسی پیدا می کنم:
همانطور که می بینین متنی که در شیء پرتاب شده اعلام شده است در داخل متغییر Message در شیء ex قرار گرفته است و من می توانم آن را نمایش دهم.
همچنین شما می توانین از طریق متغییر StackTrace کلاس Exception مسیر اتقاقات رخ داده تا زمان بروز خطا را در غالب یک رشته داشته باشین:
http://www.tabatabaei.info/csharpsamples/Exception7.gif
sunyboy
10-10-2009, 10:57 PM
تشخيص نوع خطا توسط Catch
همانطور كه در قسمت قبل اشاره شد شما مي توانيد با استفاده از Try Catch در مقابل خطاي احتمالي عكس العمل نشان دهيد. حالا به مثال زير دقت كنين:
http://www.tabatabaei.info/csharpsamples/exceptions.gif
در اين مثال كاربر شما بايد دو عدد را تايپ كرده و نرم افزار اين اعداد را كه در غالب رشته اي (string) از متد ReadLine كلاس Console گرفته شده اند - و بعد با استفاده از متد Parse به عدد تبديل گشته اند - را بر هم تقسيم كرده و نتيجه را به شما نشان مي دهد.
با توجه به كد بالا من مي توانم احتمال بروز دو نوع خطا را تشخيص دهم:
1. كاربر به جاي تايپ كردن يك عدد از رشته ها استفاده كند (مثلا بنويسد Ali)
2. كاربر يك عدد را بر 0 تقسيم نمائيد (در دات نت و بيشتر زبان هاي برنامه نويسي هيچ عددي را بر 0 نمي توان تقسيم كرد و در صورتيكه اين كار را انجام دهيم يك خطا از نوع DividedByZeroException پرتاب خواهد شد)
http://www.tabatabaei.info/csharpsamples/exceptions2.gif
نكته اي كه وجود دارد اين است كه من مي خواهم در مقابل هريك از اين نوع هاي خطا عكس العمل مناسب خودش را نشان دهم. براي اينكه بتوانم اين كار را انجام دهم بايد از چندين قسمت Catch استفاده كنم و در هر قسمت يك نوع از خطا ها را كنترل كنم:
http://www.tabatabaei.info/csharpsamples/exceptions3.gif
همانطور كه مي بينيد من در ابتدا خطاي نوع FormatException را كنترل مي كنم كه در مواقعي Raise مي شود كه شما يك رشته نا صحيح را با عدد تبديل كنين. مثالا سعي كنين حرف ABD را به عدد تبديل كنين.
در قسمت دوم من يك خطا از نوع DividedByZeroException را كنترل مي كنم كه در مواقعي ايجاد مي شود كه شما يك عدد را بر 0 تقسيم نمائيد. و در نهايت در سومین Catch هر نوع خطا ديگري كه در اين دو نوع قرار نگيرد را کنترل و یک متن عمومی را نمایش می دهد. در واقع شما می توانین با استفاده از چند قسمت Catch هر نوع خطای احتمالی را گرفته و عکس العمل مناسب در مقابل آن نمایش دهید.
به منظور دریافت متن اصلی خطا و اطلاعات دیگر خطای اصلی , شما می توانید در مقابل هر یک یک متغییر تعریف کرده و از اطلاعات آن استفاده نمائید.
http://www.tabatabaei.info/csharpsamples/exceptions4.gif
sunyboy
10-10-2009, 10:59 PM
ايجاد خطاهي خاص - Custom Exception Definition
خيلي اوقات شما مي خواهيد همراه با اعلام خطا اطلاعات ديگري را كه فقط هنگام ايجاد خطا در اختيارتان هست را هم داشته باشيد و اعلام نمايد. براي اين منظور بايد يك كلاس جديد ايجاد كرده و آن كلاس را از كلاس Exception به ارث ببريد. سپس اطلاعات اضافه مورد نياز خود را در آن كلاس به صورت ReadOnly Property تعريف كرده و آن ها را با استفاده از Constructor كلاستان مقدار دهي نمايد.
http://www.tabatabaei.info/csharpsamples/customexceptions.gif
! همانطور كه مي بينيد من در زمان ايجاد شدن شيء از اين كلاس مقدار message را به Contrcutor كلاس پدر پاس مي كنم.
در مثال زير يك كلاس به نام Person وجود دارد. تصميم گرفتم كه وقتي مقداري بيش از 100 و يا كمتر از 0 براي سن درنظر گرفته شد يك خطا پرتاب كنم. نكته اي كه وجود دارد اين است كه مي خواهم همزمان اعلام كنم كه چه سني براي چه كسي در نظر گرفته شده است كه خطا توليد شده است.
حالا در كلاس Person روي Property Age وقتي كاربر سني بيش از 100 يا كمتر از 0 را ست كند يك خطا از نوع InvalidAgeException پرتاب خواهم كرد:
http://www.tabatabaei.info/csharpsamples/customexceptions2.gif
در نتيجه وقتي به يك شيء از اين كلاس مقداري نامعتبر براي سن مشخص كنيم كاربر خطا دريافت خواهد كرد. نكته مهم اين است كه شما مي توانين اطلاعات فردي كه خطا بر روي آن اعلام شده و مقداري كه به عنوان سن براي او در نظر گرفته شده بود را هم داشته باشيد و نمايش دهيد.
http://www.tabatabaei.info/csharpsamples/customexceptions3.gif
sunyboy
10-10-2009, 10:59 PM
کلاس های Abstract در سي شارپ
قبلا در مورد Inheritance و ارث بری در سی شارپ صحبت کردم. گفتیم که در سی شارپ شما می توانید از یک کلاس به ارث برید و در صورت نیاز رفتارهای آن را override یا hide نمائید. فرض کنید که در طراحی نرم افزار پرسنلی شرکت دارید. در این سازمان دو نوع کارمند وجود دارد. کارمند ساعتی و کارمند حقوق بگیر ماهیانه. کارمندان را چگونه طراحی می کنید؟
به نظر من می توانید یک کلاس به نام Employee ایجاد کنید و اطلاعات مشترک بین هر دو نوع کارمند را در این کلاس ایجاد کنین. من در این کلاس اطلاعات نام , نام خانوادگی, سن, کد کارمندی , حقوق و یک متد برای پرداخت حقوق ایجاد کردم. سپس دو کلاس MonthlyEmployee و HourlyEmployee را از کلاس Employee به ارث بردم. نکته ای که وجود دارد این است که در کلاس Employee به وجود Salary و PrintSalary نیاز دارم اما نمی دانم که برای موجودیتی به نام کارمند که در واقع وجود خارجی ندارد(چون در سازمان من همه کارمندان یا ساعتی هستند یا حقوق بگیر و شیء از جنس کارمند محض وجود ندارد) و اینکه من نمی دانم که حقوق این نوع کارمند چگونه پرداخت و محاسبه می شود و در واقع حقوق برای کارمند محض معنی ندارد و من صرفا این خاصیت و متد را برای override کردن در کلاس های فرزند ایجاد کرده ام.
در چنین مواردی شما می توانید متد ها و property هایی که در کلاس پایه تان امکان پیاده سازیشان وجود ندارد را به صورت abstract تعریف کنین. در واقع با این کار به کامپایلر سی شارپ می فهمانید که این خاصیت یا متد صرفا به جهت override شدن در کلاس های فزرند ایجاد شده است. برای اینکه یک عضو abstract تعریف کنین باید کلمه abstract را در تعریف آن آورده و بدنه آن متد یا Property را حذف نمائید:
http://www.tabatabaei.info/csharpsamples/abstractMembers.gif
دقت فرمائید که وقتی یک کلاس دارای عضو abstract باشد آن کلاس نیز باید abstract شود.
http://www.tabatabaei.info/csharpsamples/abstractClass.gif
خصوصیات کلاس های abstract:
وقتی کلاسی دارای یک عضو abstract باشد آن کلاس هم باید abstract شود.
وقتی یک کلاس به صورت abstract ایجاد شد از آن کلاس نمی توان شیء جدید ساخت.
وقتی از یک کلاس abstract به ارث می روید باید عضو های انتزاعی (abstract members) آن را override کنید.
کلمه abstract برای یک عضو (Member) کارایی کلمه virtual را نیز داراست.
وقتی از یک کلاس abstract به ارث می روید در صورتیکه حتی یکی از عضوهای abstract آن را override نکنید آن کلاس هم باید abstract شود.
اگر از یک کلاس که یک کلاس abstract را پیاده سازی کرده به ارث برویم می توانیم دوباره اعضاء abstract آن را override کنیم.
abstract کلاس ها می توانند دارای constructor باشند.
دانلود مثال abstract class ها (http://www.tabatabaei.info/csharpsamples/AbstractClassSample.rar)
sunyboy
10-10-2009, 10:59 PM
Interface ها در سي شارپ
در پست هاي قبلي به دو سطح از Inheritance اشاره كرديم. در سطح اول يك كلاس را از يك كلاس ديگر به صورت معمولي به ارث برديم. يعني مثلا كلاس Emp را از كلاس Person به ارث برديم در حالتيكه ساختن شيءاز هر دو آن ها كاربري و منطقي بود.
سپس يك سطح انتزاعي تر كلاس هاي abstract را بررسي كرديم. كلاس Emp را به صورت abstract تعريف كرديم و كلاس هاي MonthlyEmp و HourlyEmp را از آن به ارث برديم.
حالا می خواهیم به بالاترین سطح انتزاعی در سی شارپ یعنی Interface ها بپردازیم. در بررسی اینترفیس ها من ابتدا به تعریف آن ها اشاره می کنم. سپس روش و Syntax استفاده از آن ها و در نهایت موارد استفاده از آن را بررسی می کنم.
اينترفيس ها قرارداد هایی هستند که اعلام می کنند این نوع های داده ای دارای چه امکاناتی می باشند. اما روش پیاده سازی آن ها را اعلام نمی کنند. در واقع در اینترفیس ها شما هیچ گونه پیاده سازی ندارید و فقط اعلام می کنید که نوع شما دارای چه "متد ها" , "پراپرتی ها" , "ایندکس ها" و "رویدادهایی" است.
برای تعریف اينترفيس ها در سي شارپ باید در سطح namespace همانند یک کلاس ولی با استفاده از keyword ی به نام interface.
http://www.tabatabaei.info/csharpsamples/interface.gif
همانطور که در تصویر بالا مشاهده می کنین در اینترفيس ها شما فقط به تعریف ها می پردازید و اجازه ایجاد بدنه متد ها و property ها را ندارید. در تعریف interface ها قوانین زیر وجود دارند:
امکان استفاده از access modifier ها وجود ندارد. (در واقع تمامی اعضاء یک interface) به صورت public هستند ولی کلمه public نوشته نمی شود.
در اينترفيس ها Constructor نداريم.
تمامی Property و Method و Indexer ها به صورت abstract و بدون پیاده سازی هستند.
امکان استفاده از field ها وجود ندارد.
sunyboy
10-10-2009, 11:00 PM
در قسمت قبلی در رابطه با interface ها و اینکه چگونه ایجادشان کنیم صحبت کردیم. حالا اجازه بدین در رابطه با اینکه چرا و به چه دلایلی از interface ها استفاده می کنیم صحبت کنیم.
موارد استفاده اينترفيس ها - Interface Usage
همانطور که قبلا هم اشاره شد و از کلمه inteface بر می آید در واقع اینترفيس ها یک واسط یا قرارداد هستند. اما برای اینکه راحت تر دلایل استفاده از آن ها را در زبان برنامه نویسی سی شارپ و دنیای شي گرایی متوجه بشیم من سه دلیل برای استفاده از اینترفیس ها بیان می کنم:
اینترفیس ها به عنوان استاندارد - Interface as Standard
اینترفیس ها به عنوان سرویس - Interface as Service
اینترفیس ها برای حل مشکل توارث چندگانه - Interface for Multiple Inheritance
اینترفيس ها به عنوان استاندارد - Interface as Standard
فرض بفرمائید که شما قصد تهیه یک Total System یا یک مجموعه نرم افزار یکپارچه را دارید. در این مجموعه نرم افزار Entity ها بسياری وجود دارند و در فاز تحلیل و طراحی نسبت به شناخت و طراحی آن ها اقدام کرده اید. وظيفه اجراي هر یک از این SubSystem ها توسط یک گروه از افراد در سازمان شما می باشد.
حالا موجودیتی مثل افراد (Person) را در نظر بگیرید که در تمامی زیر سيستم های شما وجود دارد و فقط با جزئیات مختلف نمود پیدا می کند. مثلا در زیر سيستم حسابداری به عنوان مشتری با اطلاعات خاص مشتري ها , در سيستم پرسنلی به عنوان کارمند با اطلاعات خاص هر کارمند و ....
نکته اینجاست که اگر قرار باشد این موجودیت ها بین زیر سیستم های این نرم افزار یکپارچه قابلیت تبادل داشته باشند باید یک استاندارد خاص در نظر گرفته شود که تمام این موجودیت های به نحوی به آن قابل تبدیل باشند. پس در این حالت یک inteface با تمامی اطلاعات مشترکي که موجودیت انسان در تمام این زیر سيستم ها دارد در نظر گرفته می شود و تمامی زير سيستم ها موظف به پیاده سازی آن توسط کلاس های خاص خود می شوند. و در صورتیکه لازم باشد یک موجود از این زير سيستم به زيرسيستم دیگر ارجاء شود به راحتی به IPerson تبدیل شده و در زير سيستم بعدی به عنوان يک IPerson دریافت و تبدیل می شود.
http://www.tabatabaei.info/csharpsamples/intefaceasstandard.gif
البته این روش نه تنها در سي شارپ بلکه در جاهای دیگر نيز استفاده دارد. به عنوان مثال وقتی قرار به استفاده از تکنولوژی Bluetooth شد 5 شرکت پيشتاز این تکنولوژی یعنی Microsoft , Ericsson و سه شرکت دیگر برای استاندارد سازی این تکنولوژی سميناري تشکیل دادند و توافق نامه ای امضاء کردند که طبق آن تمامی شرکت ها موظف به توليد محصولاتي با رعايت یکسری استاندارد شدند و البته همه آن ها می توانستند برای توسعه اين تکنولوژی اقدام کنند. در نتيجه تمامي محصولاتي که اين شرايط را رعايت کنند مي توانند با يکديگر ارتباط داشته باشند.
sunyboy
10-10-2009, 11:00 PM
اینترفیس ها به عنوان سرویس - Interface as Service
وقتی بستر دات نت را بررسی می کنید به تعداد زیادی interface برخورد می کنید که وقتی آن ها را پیاده سازی کنید , می توانید از یک سرویس استفاده کنید. در واقع به ارث بری از یک اینترفیس شما یک از سرویس برخوردار خواهید شد.
به مثال زیر دقت کنید:
http://www.tabatabaei.info/csharpsamples/arraylistsort.gif
همانطور که در تصویر بالا مشخص است شما می توانید با استفاده از متد Sort روی کلاس ArrayList که در واقع یک Collection می باشد محتویات آرایه را سورت نمائید و در نتیجه خروجی شبیه به تصویر زیر داشته باشید:
http://www.tabatabaei.info/csharpsamples/result.gif
حالا اگر در داخل یک ArrayList دیگر من چند شیء از جنس Person قرار دهم و باز متد Sort را فراخوانی کنم چه اتفاقی خواهد افتاد:
http://www.tabatabaei.info/csharpsamples/arraylistsort2.gif
همانطور که در تصویر زیر می بینید به جهت اینکه فریم ورک دات نت نمی داند که باید سورت را بر چه مبنایی روی تایپ های custom مثل Person انجام دهد یک Exception (خطا) پرتاب خواهد شد.
http://www.tabatabaei.info/csharpsamples/arraylistsort3.gif
در واقع در فریم ورک دات نت هرکجا شما به سورت کردن یک تایپ خاص مثل Person را نیاز داشتید کافیست که از یک Interface به نام IComparable به ارث برید. یا به بیانی دیگر مایکروسافت سرویس Sort را در غالب این اینترفيس ارائه می کند. حالا به کد زیر که پیاده سازی اینترفيس IComparable می باشد دقت کنید:
http://www.tabatabaei.info/csharpsamples/icomparable.gif
همانطور که می بینید در داخل این اینترفیس یک متد به نام CompareTo وجود دارد که یک ورودی از جنس Object دارد و یک خروجی عددی. در صورتیکه عددی که از این متد خارج می شود بزرگتر از یک باشد به این معناست که شیء جاری(this) بزرگتر از پارامتر پاس شده می باشد و در صورتیکه عدد کوچکتر از صفر باشد به این معناست که شیء جاری کوچکتر از پارامتر پاس شده می باشد و اگر این دو باهم برابر باشد باید خروجی متد 0 باشد. در کدی که من نوشتم مبنای مقایسه را برروی سن افراد قرار دادم که در نتیجه این نوع پیاده سازی خروجی زیر از نمونه کد بالا حاصل خواهد شد:
http://www.tabatabaei.info/csharpsamples/icomparableresult.gif
بارگزاری مثال (http://www.tabatabaei.info/csharpsamples/InterfaceAsServices.rar)
sunyboy
10-10-2009, 11:00 PM
در قسمت قبلی در رابطه با اینکه اينترفيس ها را به عنوان سرويس در نظر بگيريم صحبت شد و گفتيم که به عنوان مثال در صورتيکه شما از اينترفيس IComparable به ارث بريد و متد CompareTo را پياده سازی کنيد آنگاه مي توانيد از سرويس Sort در کلاس ArrayList استفاده کنيد. اما چرا؟
در واقع وقتي شما متد Sort را فراخواني مي کنيد در کلاس ArrayList فرض را بر این مي گذارد که تک تک اشياي داخل آرایه از این اينترفيس به ارث رفته اند در نتيجه شي داخل آرايه را (که يک object مي باشد) Cast به IComparable می کند در نتيجه مي تواند از متد CompareTo استفاده کرده و مقايسه مورد نظر را انجام دهد. حالا در صورتيکه شيء شما از اين اينترفيس به ارث نرفته باشد يک خطا از نوع InvalidOperationException دريافت خواهید کرد.
http://www.tabatabaei.info/csharpsamples/arraylistsort3.gif
نمونه های بسياري وجود دارند که شما با پياده سازي يک يا چند Interface امکان استفاده از يک موضوع (به صورت سرويس) را بهره مند مي شويد. البته به اين نکته توجه داشته باشيد که روش پياده سازي و Logic آن کاملا در اختيار شماست و در صورتيکه درست پياده سازي نشود مسئوليت خطا های احتمالی و يا عملکرد نادرست به عهده شما مي باشد.
فرض کنيد که شما يک کلاس داريد که وظيفه آن چاپ کردن اطلاعات اشياء ديگر توسط يک چاپگر مي باشد. نکته مهم اين است که شما مي خواهيد اين سرويس (یعني چاپ کردن توسط يک چاپگر خاص) را در اختيار همه قرار دهيد. براي همين منظور کافيست که يک interface طراحي کنيد و يک متد به نام Print در آن تعريف کنيد.
http://www.tabatabaei.info/csharpsamples/interfacesample1.gif
حالا کافيست که در اين کلاس از کاربران انتظار ارسال کلاس هايي را داشته باشيد که از اين اينترفيس به ارث رفته اند و در صورتيکه يک شيء به متد شما ارسال شود که از اين اينترفيس به ارث نرفته باشد شما هم يک خطا از نوع InvalidOperationException پرتاب خواهيد کرد.
http://www.tabatabaei.info/csharpsamples/interfaceSample2.gif
مثال هاي بسياري از اين دست در رابطه با استفاده از اينترفيس ها به عنوان سرويس مي توان نوشت. در پست بعدي در رابطه با استفاده از اينترفيس ها برای پياده سازي توارث چندگانه خواهم نوشت.
بارگزاري مثال (http://www.tabatabaei.info/csharpsamples/InserfaceAsService2.rar)
sunyboy
10-10-2009, 11:01 PM
اينترفيس براي توارث چندگانه - Interface for Multiple Inheritance
در پاره اي از مواقع , به اين نتيجه مي رسيم که يک موجوديت در نرم افزار شما بايد از دو يا چند موجوديت ديگر به ارث برود. اما همانطور که قبلا هم اشاره کرده بودم در سي شارپ توارث چندگانه وجود ندارد پس شما نمي توانيد از چند کلاس همزمان به ارث برويد. راه حل اين سناريو ها استفاده از اينترفيس ها براي پياده سازي توارث چندگانه مي باشد. نکته اي که وجود دارد اين است که استفاده از اين روش باعث کم تر شدن کد نويسي شما نخواهد شد.
فرض بفرمائيد که در طراحي يک سيستم براي يک شرکت توليد دو موجوديت "مشتري" و "کارمند" طراحي شده اند. هر مشتري داراي اطلاعاتي نظير نام و مبلغ اعتبار و یک متد برای خرید و يک متد براي نمايش اطلاعاتش می باشد. هر کارمند نيز داراي اطلاعاتي نظير نام و حقوق و یک متد برای نمايش اطلاعاتش می باشد. حالا شما به این نتيجه رسيده ايد که يکسري از کارمندان شرکت از شرکت خريد نيز انجام مي دهند يعني در واقع مشتري هم هستند. به همين دلیل تصميم گرفته ايد که يک کلاس به نام EmpCustomer ايجاد کنيد که هم از مشتري به ارث رفته باشد و هم از کارمند.
خوب همانطور که گفتم اجراي اين سناريو با توجه به اينکه امکان به ارث رفتن از دو يا چند کلاس به طور همزمان وجود ندارد شما بايد از يک روش ديگر يعني استفاده از interface ها اقدام کنيد.
پياده سازي توارث چندگانه با استفاده از اينترفيس ها
همانطور که قبلا اشاره کرديم يک کلاس توانايي به ارث رفتن از يک کلاس و چندين اينترفيس را دارا مي باشد. براي همين منظور من دو اينترفيس به نام هاي ICustomer و IEmployee ايجاد مي کنم:
http://www.tabatabaei.info/csharpsamples/multiple1.gif
سپس دو کلاس خود يعني Employee و Customer را از اينترفيس هاي متناظرشان به ارث مي برم و پياده سازي مي کنم:
http://www.tabatabaei.info/csharpsamples/multiple2.gif
http://www.tabatabaei.info/csharpsamples/multiple3.gif
حالا کافيست کلاس سوم را از ايجاد و از هر دو اين اينترفيس ها به ارث مي بريم:
http://www.tabatabaei.info/csharpsamples/multiple4.gif
همين طور که در تصوير مي بينيد در Print به جاي عبارت Employee یا Customer عبارت EmpCustomer چاپ خواهد شد.
!! در اين روش قصد ما اصلا کمتر نوشتن کد نمي باشد بلکه فقط پياده سازي توارث چندگانه مي باشد.
!! توجه داشته باشيد که وقتي يک کلاس از دو یا چند اينترفيس به ارث مي رود که داراي اطلاعات مشترکي هستند (مثل Name و Print در اين مثال) يکبار پياده سازي آن کافي است.
حالا کلاس Company را ايجاد مي کنم و به جاي اينکه يک آرايه از جنس Customer براي مشتريان و يک آرايه از جنس Employee براي کارمندان در نظر بگيرم آرايه اي از ICustomer براي مشتريان و آرايه اي از IEmployee براي کارمندان در نظر خواهم گرفت.
http://www.tabatabaei.info/csharpsamples/multiple5.gif
همانطور که در تصوير بالا مشاهده مي کنيد يک متد براي درج مشتريان به نام AddCustomer در نظر گرفته ام و نوع ورودي آن را از جنس ICustomer در نظر گرفته ام. همين روش براي متد AddEmployee هم با IEmployee انجام داده ام. در نتيجه شما مي توانيد اشيايي از جنس Employee و EmpCustomer را در ليست کارمندان و اشيايي از جنس Customer و EmpCustomer را در ليست مشتريان قرار دهيد.
sunyboy
10-10-2009, 11:01 PM
در مثال قبلي با پياده سازي کلاس EmpCustomer به مقصود خود رسيديم و در واقع مي توانستيم که اشيايي از اين نوع را هم به ICustomer و هم به IEmployee نسبت دهيم. حالا به کد زير دقت کنيد:
http://www.tabatabaei.info/csharpsamples/minterface1.gif
همانطور که مي بينيد من 3 شيء جديد ايجاد کرده ام. 2 تا Employee و يک EmpCustomer و آنها را به عنوان کارمند در ليست کارمندان شرکت اضافه نموده ام. و همينطور 2 شيء ديگر که از نوع Customer هستند. و به همراه شيء قبلي که از جنس EmpCustomer بود به ليست مشتريان شرکت اضافه کرده ام. حالا اگر از شيء شرکت متد هاي چاپ ليست مشتريان و چاپ ليست کارمندان را فراخواني کنم نتيجه زير را خواهيم ديد.
http://www.tabatabaei.info/csharpsamples/minterface2.gif
اين نتيجه در واقع نتيجه درستي است چرا که من 3 مشتري و 3 کارمند دارم. اما نکته اي که وجود دارد اين است که شيء EmpCustomer من در موقع نمايش اطلاعات خود ، با ما بقي اشياء من متفاوت است. يعني وقتي در ليست کارمندان نمايش داده مي شود تفاوت آن با بقيه کارمند و در ليست مشتريان با بقيه مشتريان مشهود است. اما من مي خواهم که در هر دو حالت کاملا شبيه به اين دو نوع باشد و رفتاري مشابه بقيه داشته باشد. در نتيجه من بايد از Explicit Interface Implementation استفاده کنم.
Explicit Interface Implementation
اين روش موقعي استفاده مي شود که شما مي خواهيد رفتار يک شيء را بسته به نوع reference آن تعيين کنيد. يعني وقتي به يک شيء از جنس EmpCustomer از دیدگاه ICustomer نگاه مي کنيد رفتاري شبیه مابقي ICustomer ها داشته باشد و وقتي از دید IEmployee نگاه مي کنيد رفتاري شبيه به مابقي IEmployee ها داشته باشد و در حالتي که از ديد EmpCustomer نگاه مي کنيد رفتار خاص ديگر داشت باشد. در مثال ما شما بايد متد Print را سه مرتبه پیاده سازي کنيد. به اين کد دقت کنيد:
http://www.tabatabaei.info/csharpsamples/minterface3.gif
همانطور که مي بينيد در پياده سازي هاي دوم و سوم ابتدا نام interface و سپس دقيقا اسم متد را به همان ترتيب که در interface ها نوشته شده است و بدون access modifier مي نويسيم. در پياده سازي متد نيز کاملا شبيه به Employee و Customer عمل خواهيم کرد. در نتيجه اگر مثال قبلي را دوباره اجرا کنيد خروجي به شکل زير خواهيد داشت.
http://www.tabatabaei.info/csharpsamples/minterface4.gif
بارگزاري مثال (http://www.tabatabaei.info/csharpsamples/MultipleInheritanceSample.rar)
sunyboy
10-10-2009, 11:03 PM
Delegates in CSharp
بعد از بررسی اينترفيس ها بايد به بررسي دليگيت ها بپردازيم. براي اين بررسي ابتدا من يک تعريف از Delegate خواهم گفت. سپس به روش ايجاد (Syntax) دليگيت ها مي بپردازم و در نهايت به دلايل استفاده یا مثال هاي آن خواهم پرداخت. لطفا سعي کنيد که در بررسي delegate ها کمي حوصله کنيد و با دقت مطالب را مطالعه کنيد.
delegate چيست؟
delegate ها type هايي هستند که اشياء آن ها مي توانند متد هاي کلاس هاي ديگر و متد هاي اشياء ديگر را فرخواني کنند. در واقع يک شيء از يک دليگيت براي فراخواني متد هاي کلاس ها و اشياء ديگر ايجاد مي شود.
چگونه يک delegate تعريف کنيم؟
من براي ايجاد يک delegate چهار مرحله در نظر مي گيرم.
تعريف delegate یا Delegate Definition
ايجاد reference از delegate یا Delegate Declaration
ايجاد شيء یا Delegate Initialization
فرخواني یا Calling
اجازه بدين اين مراحل را با يک مثال ساده بررسي کنيم.
مرحله اول بايد در namespace نوشته شود. همانند يک کلاس يا type هاي ديگر. وقتي مي خواهيم يک delegate را بنويسيم بايد بدانيم که اين delegate براي فراخواني چه متدهايي نوشته شده است.
http://www.tabatabaei.info/csharpsamples/delegate1.gif
همان طور که در تصوير مي بينيد من يک delegate را در فضاي namespace تعريف کرده ام. با توجه به کد نوشته شده ، اين delegate امکان فرخواني متد هايي را که خروجي ندارند (void) و همينطور هيچ پارامتري هم ندارند ، دارد. استفاده از کلمه CallBack در انتهاي نام delegate ها پيشنهاد مي شود.
حالا بايد يک reference از آن delegate ايجاد کنيم:
http://www.tabatabaei.info/csharpsamples/delegate2.gif
در مرحله سوم بايد اين reference را new کنيم:
http://www.tabatabaei.info/csharpsamples/delegate3.gif
در اين مرحله بايد شما نام يک متد و فقط نامش را به عنوان پارامتر به constructor اين delegate پاس دهيد. توجه کنيد که تمامي delegate ها داراي Constructorی با يک پارامتر مي باشند که اسم يک متد خواهد بود. متدي که پاس مي شود بايد دقيقا ساختاري شبيه به ساختار تعريف شده delegate شما (مرحله 1) داشته باشد.
مرحله آخر فراخواني delegate است:
http://www.tabatabaei.info/csharpsamples/delegate4.gif
وقتي يک شيء از يک delegate را با استفاده از () فراخواني مي کنيد در واقع متدي که داخل آن delegate تعريف شده است را فراخواني مي کنيد.
نکته مهم اين است که شما مي توانيد بيش از يک متد (با ساختار شبيه به هم) را داخل يک delegate قرار دهيد. براي اين کار به جاي استفاده از = موقع new کردن از =+ استفاده خواهيم کرد. وقتي اين delegate را فراخواني مي کنيد تمامي آن ها به ترتيب فراخواني خواهند شد.
http://www.tabatabaei.info/csharpsamples/delegate5.gif
دانلود مثال (http://www.tabatabaei.info/csharpsamples/DelegateSample1.rar)
sunyboy
10-10-2009, 11:03 PM
در مثال قبلی در رابطه با چهار مرحله توليد و استفاده يک Delegate صحبت کرديم. دقت کنيد که معمولا مراحل توليد يک delegate کنار همديگر استفاده نمي شود. و اين مراحل بين چندين کلاس پخش مي شود تا استفاده اصلي آن مشخص شود.
اجازه بدين با يک مثال ادامه بديم:
يک بانک را در نظر بگيريد. مشريان اين بانک داراي اعتبار مشخصي مي باشند. در هنگام خريد اين اعتبار کمتر و کمتر خواهد شد. اين بانک داراي n بازرس است که در سطح شعب مختلف فعاليت مي کنند. بازرسان بانک وظيفه پيگيري وضعيت اين مشتري را دارند. پس وقتي خريد مشتري از اعتبارش بيشتر مي شود بايد اطلاعات مشتري به تمامي بارزسان اعلام شود تا پيگيري هاي لازم توسط نزديک ترين بازرس انجام شود.
خوب پس من يک کلاس خواهم داشت به نام Customer و يک کلاس هم به نام Agent:
http://www.tabatabaei.info/csharpsamples/agentSample1.gif
همانطور که مي بينيد کلاس Customer داراي يک متد به نام Buy است که از اين طريق خريد انجام مي شود. نکته مهم اين است که بايد کدي بنويسيم که وقتي يک مشتري خريد مي کند تمام بازرسان متوجه خريد بيش از اعتبار وي شوند.
http://www.tabatabaei.info/csharpsamples/agentSample2.gif
در کلاس Agent يک ArrayList براي ثبت فعاليت هاي هر يک از بازرسان در نظر گرفته شده است که براي ثبت پيگيري جديد بايد از متد AddTask استفاده شود. پس ما بايد به طريقي AddTask تمامي بازرسان را همزمان و در متد Buy کلاس Customer فراخواني کنيم.
براي انجام اين موضوع من يک Delegate متناسب با متد AddTask ايجاد مي کنم (مرحله اول).
http://www.tabatabaei.info/csharpsamples/agentSample3.gif
حالا در کلاس Agent يک متغيير static (براي همه بازرسان) از جنس آن delegate ايجاد مي کنم. (مرحله دوم)
سپس در Constructor کلاس Agent متد AddTask هر يک از بازرسان را در delegate ثبت مي کنم. (مرحله سوم).
http://www.tabatabaei.info/csharpsamples/agentSample4.gif
در نهايت موقعي که اعتبار مشتري من منفي مي شود delegate static را فراخواني مي کنم. در نتيجه به تمامي بازرسان يک وظيفه جديد اضافه خواهد شد.
http://www.tabatabaei.info/csharpsamples/agentSample5.gif
و حالا کافيست چند شيء از هر کدام از کلاس ها بسازم و شروع به تست کنم:
http://www.tabatabaei.info/csharpsamples/agentSample6.gif
و در نتيجه:
http://www.tabatabaei.info/csharpsamples/agentSample7.gif
اين مثال را مرور کنيد. مسلما براي دوستاني که تازه با سي شارپ آشنا شده اند خيلي سنگين خواهد بود. اصلا مهم نيست کافيه که توي ذهنتون چند باري مرورش کنيد. به نظر من delegate سنگين ترين بحث سي شارپ است. پس اصلا به خودتون شک نکنيد!
بارگزاري مثال (http://www.tabatabaei.info/csharpsamples/AgentDelegateSample.rar)
sunyboy
10-10-2009, 11:04 PM
رويداد ها در سي شارپ - Events in CSharp
اغلب نرم افزار هايي که توليد مي شوند ساختاري Event Driven (http://en.wikipedia.org/wiki/Event-driven_programming)1 دارند. به عنوان مثال شما يک فرم ايجاد مي کنيد و کاربر با پر کردن اطلاعات فرم و در نهايت کليک بر روي گزينه ذخيره فرم اطلاعاتي مورد نظر را ذخيره مي نمايد.
پر کردن فرم ، کليک بر روي گزينه Save و ... همگي رويداد هايي هستند که از طرف کاربر شما ارجاع مي شود و چک کردن اطلاعات و ذخيره کردن اطلاعات و ... هم پاسخ (عکس العمل) هاي شما به آن رويداد ها.
براي توليد و استفاده يک رويداد در سي شارپ 7 مرحله پياده سازي وجود دارد. 5 مرحله اول براي توليد رويداد (Event Raise) و 2 مرحله آخر براي پاسخ به رويداد (Event Handler) مي باشد.
در بررسي رويداد ها با يک مثال ساده شروع مي کنيم. يک انبار را در نظر بگيريد. در اين انبار وقتي تعداد يک کالا به صفر مي رسد يک رويداد بايد اعلام شود و در نتيجه آن رويداد مسئول انبار درخواست خريد چند آيتم از آن کالا را صادر خواهد کرد. کلاس انبار را به صورت زير تعريف ميکنم. براي ثبت محصول از متد AddProduct و براي دريافت کالا از متد GetProduct استفاده مي شود. در صورتيکه تعداد کالا به صفر برسد ، رويداد مورد نظر بايد اعلام شود.
http://www.tabatabaei.info/csharpsamples/event1.gif
براي تعريف رويداد ، ابتدا يک delegate تعريف مي کنيم (مرحله اول):
http://www.tabatabaei.info/csharpsamples/event2.gif
دقت کنيد که delegate هايي که به منظور توليد Event ها ايجاد مي شوند همواره داراي دو پارامتر مي باشند. پارامتر اول از نوع object که در واقع شيء است که رويداد بر روي آن اتفاق مي افتد. پارامتر دوم از نوع EventArgs یا کلاس هاي که از آن به ارث رفته باشد. پارامتر دوم در واقع اطلاعات يا آرگومان هاي رويداد مي باشد. 2
در مرحله دوم يک event در کلاس Warehouse تعريف مي کنيم (مرحل دوم):
http://www.tabatabaei.info/csharpsamples/event3.gif
در مرحله بعدي يک متد protected به نام OnLowAmount تعريف مي کنيم (مرحله چهارم3)
http://www.tabatabaei.info/csharpsamples/event4.gif
و سپس در زمان مناسب (موقعي که تعداد کالا به صفر برسد) رويداد را با استفاده از متد protected مرحله قبل اعلام مي کنيم (مرحله پنجم):
http://www.tabatabaei.info/csharpsamples/event5.gif
سپس شروع به استفاده از اين کلاس خواهم کرد:
http://www.tabatabaei.info/csharpsamples/event6.gif
همانطور که مي بينيد در اين کلاس رويداد LowAmount به صورت يک Event (با شکلي شبيه علامت برق) مشخص شده است.
مرحله بعدي ايجاد يک متد است که با ساختار delegate رويداد مورد نظر مطابقت داشته باشد (مرحله ششم):
http://www.tabatabaei.info/csharpsamples/event7.gif
و در نهايـت وصل کردن اين متد (مرحله ششم) به رويداد با استفاده از =+ مي باشد. (مرحله هفتم):
http://www.tabatabaei.info/csharpsamples/event8.gif
دقت فرمائيد که در صورتيکه تمايل داشته باشيد مي توانيد بيش از يک متد را داخل رويداد خود به عنوان EventHandler قرار دهيد:
http://www.tabatabaei.info/csharpsamples/event9.gif
1. Event-driven programming - Wikipedia, the free encyclopedia (http://en.wikipedia.org/wiki/Event-driven_programming)
2. به عنوان مثال در رويداد KeyDown بر روي کلاس Form از کلاس KeyٍEventArgs استفاده شده است.
3. در اين مثال نيازي به وجود مرحله سوم نمي باشد.
دانلود مثال اين قسمت (http://www.tabatabaei.info/csharpsamples/EventSample.rar)
sunyboy
10-10-2009, 11:04 PM
رويداد ها و آرگيومنت هاي خاص - Event and Custom EventArgs
بسیاري از رويداد ها در هنگام وقوع داراي جزئياتي مي باشند. به عنوان مثال رويداد MouseMouse داراي اطلاعات همچون محل Cursor موس مي باشد يا در رويداد KeyDown کليدي که تايپ شده است از اطلاعات خاص اين رويداد است. ايجاد رويداد ها و پاس کردن اطلاعات خاص آن رويداد ها توسط کلاس هايي که از کلاس پايه اي به نام EventArgs به ارث رفته اند ، اعلام مي گردد.
براي بررسي اين موضوع از يک مثال استفاده مي کنيم. فرض کنيد که در مثال قبلي مي خواهيد که در موقع بروز رويداد LowAmount امکان جلوگيري از خريدي در حال وقوع را داشته باشيم. پس شما بايد يک متغيير boolean در پارامتر هاي رويدادتان به نام Cancel تعريف کنيد که در صورتي که توسط متد EventHandler به True ست شده باشد بايد از خريد جلوگيري نمائيد.
براي اجراء اين موضوع يک کلاس به نام LowAmountEventArgs تعريف مي کنيم (اين کلاس از کلاس EventArgs به ارث مي رود) و در آن يک متغيير به نام Cancel از جنس bool تعريف مي کنم:
http://www.tabatabaei.info/csharpsamples/eventArgs1.gif
سپس delegate مربوط به رويداد را به صورت زير تغيير مي دهم:
http://www.tabatabaei.info/csharpsamples/eventArgs2.gif
همچنين در زمان رويداد يک شيء از جنس LowAmountEventArgs ايجاد مي کنم:
http://www.tabatabaei.info/csharpsamples/eventArgs3.gif
حالا در زمان رويداد اين امکان وجود دارد که کاربر از ثبت اين برداشت از انبار جلوگيري کند. براي اين کار کافيست که کاربر شما در event handler مربوط به استفاده از متغيير موجود مقدار Cancel را به True ست کند.
http://www.tabatabaei.info/csharpsamples/eventArgs4.gif
نمونه هاي بسياري از اين نوع رفتار ها در دات نت وجود دارد ، به عنوان مثال در کلاس Form در Windows Application وقتي درخواست بسته شدن فرم از طرف کاربر ارسال مي شود ، يک رويداد به نام FormClosing رخ مي دهد ، در صورتيکه شما يک EventHandler براي اين رويداد بنويسيد مي توانيد با ست کردن متغيير Cancel در کلاس FormClosingEventArgs مي توانيد مانع از بسته شدن فرم شويد.
دانلود مثال اين پست (http://www.tabatabaei.info/csharpsamples/EventArgsSample.rar)
sunyboy
10-10-2009, 11:05 PM
منابع:
http://csharptuning.blogfa.com (http://csharptuning.blogfa.com/)
http://www.tabatabaei.info (http://www.tabatabaei.info/)
دل آرام...
12-03-2012, 01:17 AM
سلام ...
بسيار ممنون از مطالب مهم كه در مورد سي شارپ گذاشتين...من چن وقته كه كلاس سي شارپ ميرم به اميد اينكه بتونم ب عنوان برنامه نويس شاغل بشم... ممنون..مشتاق ادامه اين آموزش هستم.:72:
دل آرام...
02-13-2013, 03:02 PM
سلام ببخشيد من يه اشكال در بحث توارث دارم...
تو كلاس رسيديم ب مبحث توارث در سي شارپ..استادمون يه پروژه گفته اينكه كلاس دانشگاه رو ب وسيله توارث بنويسيم...يعني دانشجو كارمند.. استاد...من يه كلاس پايه ب نام انسان نوشتم كه همه كلاس هاي دانشجو و استاد و كارمند ازش ارث ميبرن...ولي نميدونم چ طوري اون رو بنويسم...مثلا كلاس دانشجو اينطوري ارث ميبره:
class student : human
{
}
دل آرام...
02-15-2013, 07:45 PM
:42:..........................:164:
Powered by vBulletin™ Version 4.2.0 Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.