توجه ! این یک نسخه آرشیو شده می باشد و در این حالت شما عکسی را مشاهده نمی کنید برای مشاهده کامل متن و عکسها بر روی لینک مقابل کلیک کنید : آموزش جامع و مرحله به مرحله زبان برنامه نويسي #c (سي شارپ)
در اين درس با ارائه چند برنامه و مثال ساده به طرز كار زبان C# ميپردازيم. اهداف اين درس عبارتند از :
فهم ساختار پايهاي يك برنامة C#
آشنايي با Namespace
آشنايي با كلاس (Class)
آشنايي با عملكرد متد Main()
آشنايي با ورودي/خروجي يا I/O
ليست 1-1، يك برنامة ساده با عنوان Welcome در زبان C#
// Namespace اعلان
using System;
// كلاس آغازين برنامه
class WelcomeCSS
{
// آغاز كار اجراي برنامه
public static void Main()
{
// نوشتن متن در خروجي
Console.WriteLine("Welcome to the C# Station Tutorial!");
}
}
برنامة ليست 1-1 داراي 4 پارامتر اصلي است، اعلان Namespace، كلاس، متد Main() و يك دستور زبان C#.
در همين جا بايد به يك نكته اشاره كنم، براي زبان C# همانند بيشتر زبانهاي برنامهسازي دو نوع كامپايلر وجود دارد. يك نوع كامپايلر كه به كامپايلر Command Line معروف است و نوع ديگر كامپايلر Visual است. كامپايلرهاي Command Line محيطي شبيه به محيط DOS دارند و با دادن يك سري دستورات به اجرا در ميآيند. كامپايلرهاي Visual محيطي همانند ويندوز دارند كه با دارا بودن محيط گرافيكي و ابزارهاي خاص، برنامهنويس را در امر برنامهسازي كمك ميكنند. از نمونههاي هر يك از كامپايلرها، ميتوان به Microsoft C# Command Line Compiler كه يك كامپايلر Command Line و Microsoft Visual C# كه يك كامپايلر Visual است، اشاره كرد. البته در حال حاضر بيشتر از كامپايلرهاي ويژوال استفاده ميشود.
من سعي ميكنم در آينده به توضيح محيط Visual C# و Visual Studio.Net بپردازم. اما فعلاً براي اجراي برنامهها ميتوانيد از Visual Studio.Net استفاده كنيد. پس از نصب آن، وارد محيط C# شده و در قسمت انتخاب برنامة جديد گزينة Console را جهت اجراي برنامهها انتخاب نماييد.
براي اين درس، فعلاً به توضيحات بيشتر دربارة محيط ويژوال نميپردازم اما در آينده به توضيح كامل محيط Visual Studio.Net خواهم پرداخت.
براي اجراي كد بالا در صورتيكه از محيط ويژوال استفاده ميكنيد بايد بر روي دكمة Run كليك كنيد و در صورتيكه كامپايلر Command Line داريد با دستور زير ميتوانيد برنامه را اجرا كنيد : csc Welcome.cs
پس از اجراي برنامه، كامپايلر براي شما يك فايل قابل اجرا(Executable) تحت نام Welcome.exe توليد ميكند.
نكته : در صورتيكه از Visual studio.Net(VS.Net) استفاده كنيد، پس از اجراي برنامه، يك صفحه براي نمايش خروجي به سرعت باز شده و بسته ميشود و شما قادر به ديدن خروخي نخواهيد بود. براي اينكه بتوانيد خروخي برنامه را ببينيد، در انتهاي برنامه دستور زير را وارد نماييد :
Console.ReadLine();
استفاده از اين دستور باعث ميشود تا برنامه منتظر دريافت يك ورودي از كاربر بماند، كه در اين حالت شما ميتوانيد خروجي برنامه خود را ديده و سپس با زدن كليد Enter برنامه را خاتمه دهيد.
نكتة ديگري كه در مورد زبان برنامهنويسي C# بايد مورد توجه قرار دهيد اينست كه اين زبان Case Sensitive است، بدين معنا كه به حروف كوچك و بزرگ حساس است يعني براي مثال ReadLine با readLine متفاوت است به طوريكه اولي جزو دستورات زبان C# و دومي به عنوان يك نام براي متغير يا يك تابع كه از طرف كاربر تعريف شده است در نظر گرفته ميشود.
اعلان NameSpace به سيستم اعلان مينمايد كه شما از توابع كتابخانهاي System جهت اجراي برنامهها خود استفاده مينماييد. دستوراتي مانند WriteLine و ReadLine جزو توابع كتابخانهاي System ميباشند. اغلب دستورات و توابع مهم و كليدي استفاده از كنسول ورودي/خروجي در اين كتابخانه ميباشد. در صورتيكه در ابتداي برنامه از using System استفاده نكنيد، يابد در ابتداي هر يك از دستورات برنامه كه مربوط اين كتابخانه است، از كلمة System استفاده نماييد. بعنوان مثال در صورت عدم استفاده از using System بايد از دستور System.Console.WriteLine() به جاي Console.WriteLine() استفاده نماييد.
تعريف كلاس،Class Welcome CSS، شامل تعريف دادهها(متغيرها) و متدها جهت اجراي برنامه است. يك كلاس، جزو معدود عناصر زبان C# است كه بوسيلة آن ميتوان به ايجاد يك شي (Object) از قبيل واسط ها (Interfaces) و ساختارها(Structures)، پرداخت. توضيحات بيشتر در اين زمينه در درسهاي آينده ذكر ميشوند. در اين برنامه كلاس هيچ داده و متغيري ندارد و تنها شامل يك متد است. اين متد، رفتار(Behavior) اين كلاس را مشخص ميكند.
متد درون اين كلاس بيان ميكند كه اين كلاس چه كاري را پس از اجرا شدن انجام خواهد داد. كلمة كليدي Main() كه نام متد اين كلاس نيز ميباشد جزو كلمات رزرو شده زبان C# است كه مشخص ميكند برنامه از كجا بايد آغاز به كار نمايد. وجود متد Main() در تمامي برنامههاي اجرايي ضروري است. در صورتيكه يك برنامه حاوي متد Main() نباشد بعنوان توابع سيستمي همانند dll هاي ويندوز در نظر گرفته ميشود.
قبل از كلمة Main() كلمه ديگري با عنوان static آورده شده است. اين كلمه در اصطلاح Modifier ميگويند. استفاده از static براي متد Main() بيان ميدارد كه اين متد تنها در در همين كلاس قابل اجراست و هيچ نمونهاي (Instance) ديگري از روي آن قابل اجرا نميباشد. استفاده از static براي متد Main() الزامي است زيرا در ابتداي آغاز برنامه هيچ نمونهاي از هيچ كلاس و شياي موجود نميباشد و تنها متد Main() است كه اجرا ميشود. (در صورتيكه با برخي اصطلاحات اين متن از قبيل كلاس، شي، متد و نمونه آشنايي نداريد، اين به دليل آنست كه اين مفاهيم جزو مفاهيم اولية برنامهنويسي شيگرا (OOP) هستند. سعي ميكنم در درسهاي آينده به توضيح اين مفاهيم نيز بپردازم، ولي فعلاً در همين حد كافي ميباشد.)
هر متد بايد داراي يك مقدار بازگشتي باشد، يعني بايد مقداري را به سيستم بازگرداند، در اين مثال نوع بازگشتي void تعزيف شده است كه نشان دهنده آنست كه اين متد هيچ مقداري را باز نميگرداند يا به عبارت بهتر خروجي ندارد. همچنين هر متد ميتواند داراي پارامترهايي نيز باشد كه ليست پارامترهاي آن در داخل پرانتزهاي جلوي آن قرار ميگيرد. براي سادگي كار در اين برنامه متد ما داراي هيچ پارامتري نيست ولي در ادامة همين درس به معرفي پارامترها نيز ميپردازم.
متد Main() رفتار و عمل خود را بوسيله Console.WriteLine(…) مشخص مينمايد. Console كلاسي در System است و WriteLine() متدي در كلاس Console. در زبان C# از اپراتور "." (نقطه dot) جهت جداسازي زيرروتينها و زيرقسمتها استفاده ميكنيم. همانطور كه ملاحظه ميكنيد چون WriteLine() يك متد درون كلاس Console است به همين جهت از "." جهت جداسازي آن استفاده كردهايم.
در زبان C#، براي قرار دادن توضيحات در كد برنامه از // استفاده ميكنيم. بدين معنا كه كامپايلر در هنگام اجراي برنامه توجهي به اين توضيحات نميكند و اين توضيحات تنها به منظور بالا بردن خوانايي متن و جهت و كمك به فهم بهتر برنامه قرار ميگيرند. استفاده از // تنها در مواردي كاربرد دارد كه توضيحات شما بيش از يك خط نباشد. در صورت تمايل براي استفاده از توضيحات چند خطي بايد در ابتداي شروع توضيحات از /* و در انتها آن از */ استفاده نماييد. در اين حالت تمامي مطالبي كه بين /* */ قرار ميگيرند به عنوان توضيحات (Comments) در نظر گرفته ميشوند.
تمامي دستورات (Statements) با ";"، سمي كولون، پايان مييابند. كلاسها و متدها با } آغاز شده و با { خاتمه مييابند. تمامي دستورات بين { } يك بلوك را ميسازند.
بسياري از برنامهها از كاربر ورودي دريافت ميكنند. انواع گوناگوني از اين وروديها ميتوانند به عنوان پارامتري براي متد Main() در نظر گرفته شوند. ليست 2-1 برنامهاي را نشان ميدهد نام كاربر را از ورودي دريافت كرده و آن را بر روي صفحه نمايش ميدهد. اين ورودي به صورت پارامتري براي متد Main() در نظر گرفته شده است.
ليست 2-1 : برنامهاي كه ورودي را از كاربر، بعنوان پارامتر Main()، دريافت ميكند.
// Namespace اعلان
using System;
// كلاس آغازين برنامه
class NamedWelcome
{
// آغاز اجرا برنامه
public static void Main(string[] args)
{
//نمايش نمايش بر روي صفحه
Console.WriteLine("Hello, {0}!", args[0]);
Console.WriteLine("Welcome to the C# Station Tutorial!");
}
}
توجه داشته باشيد كه اين برنامه، ورودي را به صورت Command-Line دريافت ميكند و در هنگام اجراي برنامه بايد ورودي را در Command-Line وارد نماييد. در صورتيكه ورودي را وارد ننماييد برنامه دچار مشكل شده و متوقف خواهد شد.
همان طور كه در ليست 2-1 مشاهده مينماييد، پارامتر متد Main() با عنوان args مشخص شده است. با استفاده از اين نام در داخل متد ميتوان آن استفاده نمود. نوع اين پارامتر از نوع آرايهاي از نوع رشته (string[]) در نظر گرفته شده است. انواع(types) و آرايهها را در درسهاي بعدي بررسي ميكنيم. فعلاً بدانيد كه آرايه رشتهاي جهت نگهداري چندين كاراكتر مورد استفاده قرار ميگيرد. [] مشخص كننده آرايه هستند كه مانند يك ليست عمل ميكند.
همانطور كه ملاحظه ميكنيد در اين برنامه دو دستور Console.WriteLine(…) وجود دارد كه اولين دستور مقداري با دستور دوم متفاوت است. همانطور كه مشاهده ميكنيد داخل دستور Console.WriteLine(…) عبارتي به شكل {0} وجود دارد. اين آرگومان، نشان ميدهد كه به جاي آن چه مقداري بايد نمايش داده شود كه در اين جا args[0] نشان داده ميشود. عبارتي كه داخل " " قرار دارد عيناً در خروجي نمايش داده ميشود، به جاي آرگومان {0}، مقداري كه پس از " قرار دارد، قرار ميگيرد. حال به آرگومان بعدي يعني args[0] توجه كنيد. مقدار صفر داخل [] نشان ميدهد كه كدام عنصر از آرايه مورد استفاده است. در C# انديس آرايه از صفر شروع ميشود به همين جهت براي دسترسي به اولين عنصر آرايه بايد از انديس صفر استفاده كنيم.(همانطور كه قبلاً نيز كفتم آرايهها را در درسهاي آينده توضيح خواهم داد، هدف از اين درس تنها آشنايي با C# است.!)
مجدداً به آرگومان {0} بازگرديم. اين آرگومان نشان مي دهد كه يك مقدار بايد در رشته خروجي قرار گيرد، اين مقدار همان args[0] است. اگر شما اين برنامه را از طريق Command-Line اجرا نماييد خروجي شبيه به زير خواهيد گرفت :
>Hello!, Meysam!
>Welcome to C# Station Tutorial!
همان گونه كه ميبينيد، پس از اجراي برنامه نام شما كه از طريق Command-Line آنرا وارد نمودهايد در خروجي ظاهر ميشود. استفاده از آرگومان {n}، كه در آن n يك مقدار عددي است، جهت فرمت دادن به متن خروجي است كه بر روي صفحه به نمايش در ميآيد. مقدار n از صفر آغاز شده و به ترتيب افزايش ميبايد. به مثال زير توجه كنيد :
Console.WriteLine("Hello! ,{0} ,{1}, {2}",args[0],args[1],args[2]); اين خط از برنامه سه مقدار args[0],args[1],args[2] را در خروجي به ترتيب نمايش ميدهد. ملاحظه مينماييد كه چون 3 مقدار را ميخواهيم نمايش دهيم، سه بار از آرگومان {n} استفاده كردهايم و هر بار يك واحد به مقدار قبلي افزودهايم. در آينده بيشتر با اين مفاهيم آشنا ميشويم.
مطلبي كه بايد در مورد ليست 2-1 به آن توجه شود آنست كه اين برنامه تنها از طريق Command-Lineقابل اجراست و در صورتيكه كاربر از اين مطلب كه برنامه بايد داراي ورودي به صورت Command-Line باشد، بي اطلاع باشد و ورودي را در Command-Line وارد نكند، برنامه متوقف شده و اجرا نميشود. پس براي رفع چنين مشكلي بايد از روش بهتري جهت دريافت ورودي از كاربر استفاده كرد.
ليست 3-1 : يك برنامه كه قابليت محاوره با كاربر را دارد.
// Namespace اعلان
using System;
// كلاس آغازين برنامه
class InteractiveWelcome
{
//آغاز اجراي برنامه
public static void Main()
{
// متني بر روي صفحه نمايش داده ميشود
Console.Write("What is your name?: ");
//متني نمايش داده شده و برنامه منتظر دريافت ورودي ميماند
Console.Write("Hello, {0}! ", Console.ReadLine());
Console.WriteLine("Welcome to the C# Station Tutorial!");
}
}
همانطوريكه در اين برنامه ديده مي شود، متد Main() داراي پارامتر نيست. در عوض يك خط به متن برنامه ليست 2-1 اضافه شده است. در اولين خط از اين برنامه، متني با عنوان اينكه نام شما چيست؟ بر روي صفحه ظاهر ميشود. سپس در خط بعدي پس از نوشتن كلمه Hello, ، برنامه منتظر دريافت ورودي از كاربر ميشود. بدين معني كه اين بار تا زمانيكه كاربر متني را به عنوان نام خود وارد نكند اجراي برنامه به پيش نخواهد رفت و خط بعدي اجرا نميشود. اين برنامه روش ايجاد ارتباط از طريق برنامه با كاربر را نمايش ميدهد. در اين مثال كاربر دقيقاً متوجه ميشود كه چه زماني بايد اطلاعات را وارد نمايد و اين اطلاعات چه بايد باشد در حاليكه در مثال قبل چنين نبود. همانگونه كه ميبينيد در اين برنامه آرگومان {0} مستقيماً از طريق دستور Console.ReadLine() دريافت ميشود و بلافاصله در خروجي نمايش داده ميشود. دستور ReadLine() نيز يكي از متدهاي كلاس Console است كه بوسيله آن رشته ورودي خوانده ميشود.
خروجي برنامه فوق به شكل زير است :
What is your name?:
(سپس برنامه منتظر دريافت متني از ورودي توسط كاربر ميماند)
(پس از اينكه كاربر رشتهاي را وارد كرد و كليدEnter را فشار داد، متن زير نمايش داده ميشود.)
Hello, Meysam!
(سپس اجراي برنامه به دستور بعدي منتقل ميشود)
Welcome to the C# Station Tutorial!
خروجي كامل برنامه :
What is your name?:
Hello, Meysam! Welcome to the C# Station Tutorial!
توجه كنيد كه ReadLine() به عنوان يك متد، مقداري را به سيستم بازميگرداند. اين مقدار در اين برنامه به آرگومان {0} برگردانده ميشود. اين خط از برنامه را ميتوان طور ديگري هم نوشت :
string myName=Console.ReadLine();
Console.WriteLine("Hello, {0}!",myName);
در اين حالت ما يك متغير از نوع رشته با نام myName تعريف كردهايم كه مقدار ورودي در آن ذخيره ميشود و سپس از اين مقدار به عنوان مقداري كه {0} ميپذيرد استفاده كردهايم.
در اين درس آموختيد كه ساختار كلي يك برنامه C# چگونه است. هر برنامه C# از يك كلاس اصلي تشكيل ميشود كه اين كلاس شامل دادهها و متغيرها و متدهايي ميباشد. متد آغازين برنامه كه برنامه با آن شروع به اجرا ميكند، متد Main() است. با استفاده از توابع كتابخانهاي مينوان به كلاسها و متدهاي C# دسترسي پيدا كرد. از جمله اين توابع System بود كه يكي از كلاسهاي آن Console و چند متد اين كلاس، متدهاي WriteLine() و ReadLine() بودند.
درس دوم – عبارات، انواع و متغيرها در C#
در اين درس به معرفي عبارات، انواع و متغيرها در زبان C# ميپردازيم. هدف از اين درس بررسي موارد زير است :
آشنايي با متغيرها
فراگيري انواع(Types) ابتدايي در C#
فراگيري و درك عبارات(Expressions) در C#
فراگيري نوع رشتهاي(String) در زبان C#
فراگيري چگونگي استفاده از آرايهها(Arrays) در زبان C#
متغيرها، به بيان بسيار ساده، مكانهايي جهت ذخيره اطلاعات هستند. شما اطلاعاتي را در يك متغير قرار ميدهيد و از اين اطلاعات بوسيله متغير در عبارات C# استفاده مينماييد. كنترل نوع اطلاعات ذخيره شده در متغيرها بوسيله تعيين كردن نوع براي هر متغير صورت ميپذيرد.
C# زباني بسيار وابسته به انواع است، بطوريكه تمامي عملياتي كه بر روي دادهها و متغيرها در اين زبان انجام ميگيرد با دانستن نوع آن متغير ميسر ميباشد. قوانيني نيز براي تعيين اينكه چه عملياتي بر روي چه متغيري انجام شود نيز وجود دارد.(بسته به نوع متغير)
انوع ابتدايي زبان C# شامل : يك نوع منطقي(Boolean) و سه نوع عددي اعداد صحيح(integer)، اعداد اعشاري(Floating points) و اعداد دسيمال(Decimal) ميباشد.(به انواع Boolean از اينرو منطقي ميگوييم كه تنها داراي دو حالت منطقي صحيح(True) و يا غلط(False) ميباشند.)
مثال 1 – نشان دادن مقادير منطقي (Boolean)
using System;
class Booleans
{
public static void Main()
{
bool content = true;
bool noContent = false;
Console.WriteLine("It is {0} that C# Persian provides C# programming language
content.", content);
Console.WriteLine("The statement above is not {0}.", noContent);
}
}
در اين مثال، مقادير منطقي متغيرهاي Boolean به عنوان قسمتي از جمله در خروجي نمايش داده ميشوند. متغيرهاي bool تنها ميتوانند يكي از دو مقدار true يا false را داشته باشند، يعني همانند برخي از زبانهاي برنامهسازي مشابه، مانند C و يا C++، مقدار عددي نميپذيرند، زيرا همانگونه كه ميدانيد در اين دو زبان هر مقدار عددي صحيح مثبت بغير از صفر به عنوان true و عدد صفر به عنوان false در نظر گرفته ميشود و در حقيقت نوع bool در اين دو زبان نوعي integer ميباشند. اما در زبان C# انواع bool يكي از دو مقدار true يا false را ميپذيرند. خروجي برنامه بالا به صورت زير است :
It is True that C# Persian provides C# programming language content.
The statement above is not False.
جدول زير تمامي انواع عددي صحيح C#، اندازه آنها و رنج قابل قبول آنها را نشان ميدهد.
رنج قابل قبول
اندازه به بيت
نوع
128- تا 127
8
sbyte
0 تا 255
8
byte
32768- تا 32767
16
short
0 تا 65535
16
ushort
2147483648- تا 2147483647
32
int
0 تا 4294967295
32
uint
9223372036854775808- تا 9223372036854775807
64
long
0 تا 18446744073709551615
64
ulong
از اين انواع براي محاسبات عددي استفاده ميگردد. يك نوع ديگر را نيز ميتوان در اين جدول اضافه نمود و آن نوع char است. هر چند شايد از نظر بسياري از دوستاني كه با زبانهاي ديگر برنامهسازي كار كردهاند اين تقسيم بندي غلط به نظر آيد، اما بايد گفت كه در زبان C# نوع char نيز نوع خاصي از انواع عددي است كه رنجي بين صفر تا 65535 دارد و اندازه آن نيز 16 بيتي است، اما به جاي نمايش دادن مقادير عددي تنها ميتواند بيان كننده يك كاراكتر باشد. در آينده در اين مورد بيشتر توضيح خواهم داد.
جدول زير تمامي انواع عددي اعشاري زبان C# را نمايش ميدهد.
رنج قابل قبول
دقت
اندازه به بيت
نوع
تا
7 رقم
32
float
تا
15-16 رقم
64
double
تا
28-29 رقم دسيمال
128
decimal
انواعي از نوع floating point هنگامي استفاده ميشوند كه محاسبات عددي به دقتهاي اعشاري نياز داشته باشند. همچنين براي منظورهاي تجاري استفاده از نوع decimal بهترين گزينه است. اين نوع تنها در زبان C# وجود دارد و در زبانهاي مشابه به آن نظير Java چنين نوعي در نظر گرفته نشده است.
در يك زبان برنامهسازي نتايج بوسيله ايجاد يك سري عبارت توليد ميگردند. عبارات از تركيب متغيرها و عملگرها در دستورالعملهاي يك زبان ايجاد ميگردند.(توجه نماييد كه عبارت معادل expression و دستورالعمل معادل statement ميباشد كه ايندو با يكديگر متفاوت ميباشند.) جدول زير عملگرهاي موجود در زبان C#، حق تقدم آنها و شركتپذيري آنها را نشان ميدهد.
شركتپذيري
عملگر(ها)
نوع عمل
از چپ
(x) x.y f(x) a[x] x++ x--
new typeof sizeof checked unchecked
عمليات ابتدايي
از چپ
+ - ! ~ ++x --x (T)x عمليات يكاني
از چپ
* / %
عمليات ضربي
از چپ
- +
عمليات جمعي
از چپ
<< >>
عمل شيفت
از چپ
< > <= >= is
عمليات رابطهاي
از راست
== !=
عمليات تساوي
از چپ
&
عمل AND منطقي
از چپ
|
عمل OR منطقي
از چپ
^
عمل XOR منطقي
از چپ
&&
عمل AND شرطي
از چپ
||
عمل OR شرطي
از چپ
?:
عمل شرطي
از راست
= *= /= %= += -= <<= >>= &= ^= |=
عمل انتساب
شركتپذيري از چپ بدين معناست كه عمليات از چپ به راست محاسبه ميشوند. شركتپذيري از راست بدين معناست كه تمامي محاسبات از راست به چپ صورت ميگيرند. به عنوان مثال در يك عمل تساوي، ابتدا عبارات سمت راست تساوي محاسبه شده و سپس نتيجه به متغير سمت چپ تساوي تخصيص داده ميشود.
مثال 2- عملگرهاي يكاني (Unary)
using System;
class Unary
{
public static void Main()
{
int unary = 0;
int preIncrement;
int preDecrement;
int postIncrement;
int postDecrement;
int positive;
int negative;
sbyte bitNot;
bool logNot;
preIncrement = ++unary;
Console.WriteLine("Pre-Increment: {0}", preIncrement);
preDecrement = --unary;
Console.WriteLine("Pre-Decrement: {0}", preDecrement);
postDecrement = unary--;
Console.WriteLine("Post-Decrement: {0}", postDecrement);
postIncrement = unary++;
Console.WriteLine("Post-Increment: {0}", postIncrement);
Console.WriteLine("Final Value of Unary: {0}", unary);
positive = -postIncrement;
Console.WriteLine("Positive: {0}", positive);
negative = +postIncrement;
Console.WriteLine("Negative: {0}", negative);
bitNot = 0;
bitNot = (sbyte)(~bitNot);
Console.WriteLine("Bitwise Not: {0}", bitNot);
logNot = false;
logNot = !logNot;
Console.WriteLine("Logical Not: {0}", logNot);
}
}
به هنگام محاسبه عبارات، دو عملگر x++ و x—(كه در اينجا كاراكتر x بيان كننده آن است كه عملگرهاي ++ و – در جلوي عملوند قرار ميگيرند post-increment و post-decrement) ابتدا مقدار فعلي عملوند (operand) خود را باز ميگرداند و سپس به عملوند خود يك واحد اضافه كرده يا از آن يك واحد ميكاهند. عملگر ++ يك واحد به عملوند خود اضافه ميكند و عملگر – يك واحد از عملوند خود ميكاهد. بدين ترتيب عبارت x++ معادل است با عبارت x=x+1 و يا x+=1 اما همانطور كه گفته شد بايد توجه داشته باشيد كه اين عملگرها(++ و --) ابتدا مقدار فعلي عملوند خود را برگشت ميدهند و سپس عمل خود را روي آنها انجام ميدهند. بدين معني كه در عبارت x=y++ در صورتيكه در ابتداي اجراي برنامه مقدار x=0 و y=1 باشد، در اولين اجراي برنامه مقدار x برابر با 1 يعني مقدار y ميشود و سپس به متغير y يك واحد افزوده ميشود، در صورتيكه اگر اين عبارت را بصورت x=++y بنويسيم در اولين اجراي برنامه، ابتدا به مقدار متغير y يك واحد افزوده ميشود و سپس اين مقدار به متغير x تخصيص داده ميشود كه در اين حالت مقدار متغير x برابر با 2 ميشود.(در مورد عملگر – نيز چنين است.) پس با اين توضيح ميتوان گفت كه دو عملگر ++x و –x ابتدا به عملوند خود يك واحد اضافه يا يك واحد از آن كم ميكنند و سپس مقدار آنها را باز ميگردانند.
در مثال 2، مقدار متغير unary در قسمت اعلان برابر با 0 قرار گرفته است. هنگاميكه از عملگر ++x استفاده ميكنيم، به مقدار متغير unary يك واحد افزوده ميشود و مقدارش برابر با 1 ميگردد و سپس اين مقدار، يعني 1، به متغير preIncrement تخصيص داده ميشود. عملگر –x مقدار متغير unary را به 0 باز ميگرداند و سپس اين مقدار را به متغير preDecrement نسبت ميدهد.
هنگاميكه از عملگر x-- استفاده ميشود، مقدار متغير unary، يا همان مقدار صفر، به متغير postDecrement تخصيص داده ميشود و سپس از مقدار متغير unary يك واحد كم شده و مقدار اين متغير به 1- تغيير ميكند. سپس عملگر x++ مقدار متغير unary، يعني همان 1-، را به متغير postIncrement تخصيص ميدهد و سپس يك واحد به مقدار متغير unary ميافزايد تا مقدار اين متغير برابر با 0 (صفر) شود.
مقدار متغير bitNot در هنگام اعلان برابر با صفر است. با استفاده از عملگر نقيض بيتي (~) (يا عملگر مكملگيري) متغير bitNot بعنوان يك بايت در نظر گرفته ميشود و مقدار آن منفي يا نقيض ميشود. در عمليات بيتي نقيض بدين معناست كه تمامي يكها به صفر و تمامي صفرها به يك تبديل شوند. در اين حالت نمايش باينري عدد صفر يا همان 00000000 به نقيض آن يعني 11111111 تبديل ميگردد.
در اين مثال به عبارت (sbyte)(~bitNot) توجه نماييد. هر عملي كه بر روي انواع short،unshort ، byte و sbyte انجام شود، مقداري از نوع int را باز ميگرداند. بمنظور اينكه بتوانيم نتيجه دلخواه را به متغير bitNot تخصيص دهيم بايد از فرمت (Type) operator استفاده نماييم كه در آن Type نوعي است ميخواهيم نتيجه ما به آن نوع تبديل شود و operator عملي است كه بر روي متغير صورت ميپذيرد. به بيان ديگر چون ميخواهيم مقدار متغير bitNot بصورت بيتي در نظر گرفته شود، پس بايد نتيجه عمل ما بصورت بيتي در آن ذخيره شود كه استفاده از نوع sbyte باعث ميشود تا نتيجه به فرم بيتي (يا بايتي) در متغير ما ذخيره شود. بايد توجه نماييد كه استفاده از فرمت (Type) يا در اصطلاح عمل Casting، در مواقعي كه ميخواهيم تغييري از يك نوع بزرگتر به نوع كوچكتر ايجاد نماييم، مورد استفاده قرار گيرد، چرا كه در اين حالات ممكن است با از دست دادن اطلاعات مواجه باشيم. در اين مثال چون ميخواهيم نوع بزرگتر int را به(32 بيتي) به نوع كوچكتر sbyte (8 بيتي) تبديل نماييم، بدين منظور بايد بطور صريح از عمل Casting استفاده نماييم تا اطلاعاتي در اين تبديل از بين نرود. در مورد تبديل انواع كوچكتر به انواع بزرگتر(مثلا تبديل sbyte به int) نيازي به استفاده از عمل Casting نيست چرا كه امكان از بين رفتن اطلاعات وجود ندارد. در ضمن بايد به يك نكته مهم توجه نماييد و آن تبديل انواع علامتدار(Signed) و بدون علامت(Unsigned) به يكديگر است. در اين حالت خطر بسيار مهمي دادههاي شما را تهديد مينمايد. بحث در مورد مسائل پيچيدهتر در مورد تبديل انواع علامتدار و و بدون علامت به يكديگر در اينجا نميگنجد و سعي ميكنم تا آنها را در مطالب بعدي و در جاي لازم مورد بحث و بررسي قرار دهم.(در صورتيكه برخي از مطالب اين قسمتها براي شما به خوبي قابل درك نيست، نگران نباشيد چراكه در آينده در مثالهايي كه خواهيد ديد تمامي اين مطالب را در عمل نيز حس كرده و با آنها آشنا خواهيد شد.)
عملگر بعدي كه در اين برنامه مورد استفاده قرار گرفته است، عملگر نقيض منطقي يا همان "!" است كه امكان تغيير مقدار يك متغير Boolean را از true به false و بالعكس را فراهم ميآورد. در مثال بالا(مثال شماره 2) مقدار متغير logNot پس از استفاده از عملگر "!" از false به true تغيير كرده است. با توجه به توضيحات اخير خروجي زير از برنامه مثال 2 مورد انتظار است :
Pre-Increment: 1
Pre-Decrement 0
Post-Decrement: 0
Post-Increment -1
Final Value of Unary: 0
Positive: 1
Negative: -1
Bitwise Not: -1
Logical Not: True
مثال 3 – عملگرهاي دوتايي
}
using System;
class Binary
{
public static void Main()
{
int x, y, result;
float floatResult;
x = 7;
y = 5;
result = x+y;
Console.WriteLine("x+y: {0}", result);
result = x-y;
Console.WriteLine("x-y: {0}", result);
result = x*y;
Console.WriteLine("x*y: {0}", result);
result = x/y;
Console.WriteLine("x/y: {0}", result);
floatResult = (float)x/(float)y;
Console.WriteLine("x/y: {0}", floatResult);
result = x%y;
Console.WriteLine("x%y: {0}", result);
result += x;
Console.WriteLine("result+=x: {0}", result);
}
خروجي اين برنامه به فرم زير است :
x+y: 12
x-y: 2
x*y: 35
x/y: 1
x/y: 1.4
x%y: 2
result+=x: 9
مثال 3 استفادههاي متفاوتي از عملگرهاي دوتايي را نشان ميدهد.(منظور از عملگر دوتايي، عملگري است كه داراي دو عملوند ميباشد مانند عملگر جمع "+"). بسياري از عملگرهاي مورد استفاده در اين مثال عملگرهاي رياضي هستند و نتيجه عمل آنها مشابه عملي است كه از آنها در رياضيات ديدهايد. از نمونه اين عملگرها ميتوان به عملگرهاي جمع "+"، تفريق "-"، ضرب "*" و تقسيم "/" اشاره نمود.
متغير floatResult از نوع اعشاري يا float تعريف شده است. در اين مثال نيز صريحاً از عمل Casting جهت اسفاده از دو متغير x و y كه از نوع int هستند، براي انجام عملي كه نتيجهاش از نوع float است، استفاده كردهايم.
در اين مثال از عملگر "%" نيز استفاده كردهايم. اين عملگر در عمليات تقسيم كاربرد دارد و باقيمانده تقسيم را برميگرداند. يعني دو عملوند خود را بر يكديگر تقسيم ميكند و باقيمانده اين تقسيم را برميگرداند.
در اين مثال همچنين فرم جديدي از عمل انتساب را بصورت result+=x مشاهده مينماييد. استفاده از عملگرهاي انتسابي كه خود تركيبي از دو عملگر هستند، جهت سهولت در امر برنامهنويسي مورد استفاده قرار ميگيرند. عبارت فوق معادل result = result+x ميباشد. يعني مقدار قبلي متغير result با مقدار متغير x جمع ميشود و نتيجه در متغير result قرار ميگيرد.
يكي ديگر از انواعي كه تا كنون با آن سر و كار داشتهايم نوع رشتهاي (string) است. يك رشته، از قرار گرفتن تعدادي كاراكتر در كنار يكديگر كه داخل يك زوج كوتيشن " " قرار گرفتهاند، ايجاد ميگردد. بعنوان مثال "Hi This is a string type". در اعلان متغيرها نيز در صورت تعريف متغيري از نوع رشتهاي، در صورت نياز به تخصيص مقدار به آن، حتماً كاراكترهايي كه ميخواهيم بعنوان يك رشته به متغيرمان نسبت دهيم را بايد داخل يك زوج كوتيشن " " قرار دهيم. به مثال زير توجه نماييد.
string Name;
…
Name = "My name is Meysam";
همانطور كه در اين مثال مشاهده مينماييد، متغيري از نوع رشتهاي تحت نام Name تعريف شده است و سپس در جايي از برنامه كه نياز به تخصيص مقدار براي اين متغير وجود دارد، عبارت مورد نظر را داخل دو كوتيشن قرار داده و به متغير خود تخصيص دادهايم. رشتهها از پر كاربرد ترين انواع در زبانهاي برنامهسازي جهت ايجاد ارتباط با كاربر و دريافت اطلاعات از كاربر ميباشند.(همانطور كه در درس قبل اول نيز گفته شد، دستور Console.ReadLine() يك رشته را از ورودي دريافت مينمايد.) در مثالهايي كه در طي درسهاي اين سايت خواهيد ديد، نمونههاي بسياري از كاربرد انواع مختلف و نيز نوع رشتهاي را خواهيد ديد.
آرايهها(Arrays)
يكي ديگر از انواع دادهاي در زبان C#، آرايهها (Arrays) ميباشند. يك آرايه را به عنوان مخزني براي نگهداري اطلاعات در نظر ميگيريم كه داراي ليستي از محلهايي است كه در آنها اطلاعات ذخيره شده است و از طريق اين ليست ميتوان به اطلاعات آنها دسترسي پيدا نمود. به هنگام اعلان آرايهها بايد نوع، اندازه و تعداد بعد آنها را نيز معين نمود.
مثال 4- آرايهها و عمليات بر روي آنها
using System;
class Array
{
public static void Main()
{
int[] myInts = { 5, 10, 15 };
bool[][] myBools = new bool[2][];
myBools[0] = new bool[2];
myBools[1] = new bool[1];
double[,] myDoubles = new double[2, 2];
string[] myStrings = new string[3];
Console.WriteLine("myInts[0]: {0}, myInts[1]: {1}, myInts[2]: {2}", myInts[0],
myInts[1], myInts[2]);
myBools[0][0] = true;
myBools[0][1] = false;
myBools[1][0] = true;
Console.WriteLine("myBools[0][0]: {0}, myBools[1][0]: {1}", myBools[0][0],
myBools[1][0]);
myDoubles[0, 0] = 3.147;
myDoubles[0, 1] = 7.157;
myDoubles[1, 1] = 2.117;
myDoubles[1, 0] = 56.00138917;
Console.WriteLine("myDoubles[0, 0]: {0}, myDoubles[1, 0]: {1}", myDoubles[0, 0], myDoubles[1, 0]);
myStrings[0] = "Joe";
myStrings[1] = "Matt";
myStrings[2] = "Robert";
Console.WriteLine("myStrings[0]: {0}, myStrings[1]: {1}, myStrings[2]: {2}",
myStrings[0], myStrings[1], myStrings[2]);
}
}
خروجي مثال 4 بصورت زير است :
myInts[0]: 5, myInts[1]: 10, myInts[2]: 15
myBools[0][0]: True, myBools[1][0]: True
myDoubles[0, 0]: 3.147, myDoubles[1, 0]: 56.00138917
myStrings[0]: Joe, myStrings[1]: Matt, myStrings[2]: Robert
در اين مثال انواع مختلفي از آرايهها اعلان شدهاند. در ابتدا يك آرايه تك بعدي، سپس آرايهاي دندانهدار و در نهايت نيز يك آرايه دو بعدي در اين مثال اعلان شدهاند.
اولين اعلان در اين برنامه مربوط به اعلان آرايه تك بعدي myInts ميباشد كه از نوع int بوده و داراي 3 عضو ميباشد كه تعداد اين اعضا با اعلان چند مقدار در داخل { } معين شده است. همانطور كه از اين اعلان دريافت ميشود، آرايه تك بعدي بصورت زير تعريف ميشود :
type[] arrayName;
كه در آن type نوع آرايه و arrayName نام آرايه ايست كه تعريف مينمائيم.
اما در ابتدا گفته شد كه به هنگام اعلان آرايهها اندازه آنها نيز بايد مشخص شود. براي تعيين اندازه آرايه، يعني تعدا عناصري كه آرايه در خود جاي ميدهد، ميتوان به چند روش عمل نمود. اولين و سادهترين روش كه در اين مثال نيز آورده شده است، تخصيص مقاديري به آرايه در داخل يك زوج { } است. بسته به نوع آرايه، تعداد عناصري كه داخل اين زوج { } قرار ميگيرند، تعداد عناصر آرايه ميباشند و مقادير عناصر آرايه نيز همان مقاديري است كه داخل { } قرار گرفته است. به عنوان مثال در مثال 4، اولين آرايه ما داراي 3 عنصر است كه مقادير آنها به ترتيب برابر با 5، 10 و 15 ميباشد.
روش ديگر جهت تعيين اندازه آرايه استفاده از روش تعريف كامل آرايه است كه به فرم كلي زير ميباشد.
type[] arrayName = new type[n];
كه در اين تعريف، استفاده از كلمه كليدي new باعث ايجاد نمونهاي جديد از نوع مورد نظر، ميشود. n نيز تعداد عناصر آرايه است كه ميخواهيم آنرا توليد نماييم. در اين حالت بايد توجه داشت كه آرايهاي تهي را توليد نمودهايم و هيچ عنصري را در آرايه جاي ندادهايم و در برنامه بايد آرايه را مقدار دهي نماييم. به مثال زير توجه كنيد.
int[] myArray = new int[15];
اين مثال آرايهاي تك بعدي از نوع int را با اندازه 15 عنصر توليد مينمايد. يعني اين آرايه قادر است تا 15 عنصر از نوع int را در خود ذخيره نمايد.
گونه ديگري از آرايهها، آرايههاي چند بعدي (Multi Dimensional Arrays) هستند كه براي نگهداري اطلاعات از چندين بعد استفاده ميكنند و بيشتر براي نگداري جداول و ماتريسها مورد استفاده قرار ميگيرند. فرم كلي اعلان اين آرايهها بصورت زير است :
type[ , , … , ] arrayName = new type[n1, n2, …. , nm];
كه در آن تعداد ابعاد آرايه با ويرگول مشخص شده و n1 تا nm نيز تعداد عناصر هر يك از ابعاد است. بعنوان مثال تعريف يك آرايه سه بعدي به فرم زير است :
char[ , , ] charArray = new char[3,5,7];
در اين مثال يك آرايه سه بعدي از نوع char توليد كردهايم كه ابعاد آن به ترتيب داراي 3، 5 و 7 عنصر ميباشند.
نوع ديگري از آرايهها، آرايههاي دندانهدار (Jagged Arrays) هستند. اين نوع آرايهها تنها در زبان C# وجود دارند و در صرفهجويي حافظه بسيار موثر ميباسند. يك آرايه دندانهدار، در حقيقت يك آرايه تك بعدي است كه هر يك از اعضاي آن خود يك آرايه تك بعدي ميباشند. اندازه اين عناصر ميتواند متفاوت باشد و تفاوت اين آرايهها با آرايههاي چند بعدي در همين جا نمايان ميشود. استفاده از اين آرايهها در مواردي كاربرد دارد كه نيازي نيست تا تمامي ابعاد آرايه داراي تعداد عناصر مساوي باشند. بعنوان مثال فرض كنيد ميخواهيد آرايهاي جهت نگهداري تعداد روزهاي ماههاي مختلف سال تهيه كنيد. در صورتيكه بخواهيد از آرايه چند بعدي استفاده نماييد، چون تعداد روزهاي تمامي ماههاي سال يكسان نيست، مجبوريد تا تعداد عناصر تمام بعدهاي آرايه را برابر با بزرگترين تعداد روز ماهها، يعني 31، تعريف نماييد. ولي چون تنها 6 ماه داراي 31 روز ميباشند، براي 6 ماه ديگر تعدادي از عناصر آرايه هيچگاه مورد استفاده قرار نميگيرند و حافظه را به هدر دادهايم. اما در صورتيكه براي اين مثال از آرايههاي دندانهدار استفاده نماييم، ميتوانيم يك آرايه دندانهدار 12 عنصري تعريف نماييم و سپس تعداد عناصر هر يك از اعضاي آنرا برابر با تعداد روزهاي ماه مورد نظر تعريف كنيم :
با استفاده از آرايه چند بعدي :
int[ , ] monthArray = new int[12,31];
با استفاده از آرايه دندانهدار :
int[][] monthArray = new int[12][];
در تعريف اول كه در آن از آرايه چند بعدي استفاده كرديم، مشاهده ميكنيد كه آرايهاي دو بعدي تعريف كردهايم كه بعد اول آن 12 عضو و بعد دوم آن 31 عضو دارد. اين عمل دقيقاً همانند ايجاد يك جدول براي نگهداري روزهاي ماههاي سال است.
اما در حالت دوم كه در آن از آرايه دندانهدار بهره بردهايم، يك آرايه تعريف نمودهايم كه بعد اول آن 12 عضو دارد ولي بعد دوم آنرا را تعريف نكردهايم كه داراي چند عضو است و هر يك از عناصر بعد اول آرايه ميتواند داراي تعداد اعضاي متفاوتي باشد كه با استفاده از اين روش ميتوان به هر يك از ماههاي سال تعداد روزهاي مورد نظر آن ماه را تخصيص داد و فضايي بلا استفاده ايجاد نخواهيم كرد. توجه نماييد كه چون تعداد عناصر بعد ديگر اين آرايه معين نشده است در برنامه بايد اين تعداد عنصر را مشخص نماييم :
monthArray[1] = new int[31];
monthArray[10] = new int [30];
مشاهده ميكنيد كه به هر ماه، تعدا عنصر مورد نياز خود را تخصيص دادهايم. تنها بايد به تفاوت اعلان آرايههاي دندانهدار با آرايههاي چند بعدي توجه نماييد.
دسترسي به عناصر آرايه از طريق انديس امكان پذير است. انديس شماره محل ذخيرهسازي دادههاي ما ميباشد كه با دادن اين شماره ميتوانيم به داده مورد نظر دسترسي پيدا كنيم. در C# همانند C و C++ انديس خانههاي آرايه از صفر آغاز ميگردد يعني اولين خانه آرايه داراي شماره صفر است و عناصر بعدي به ترتيب يك واحد به انديسشان اضافه ميگردد. پس شماره انديس آرايه هميشه يك واحد كمتر از تعداد عناصر آن است، يعني آرايهاي كه 10 عضو دارد بزرگترين انديس خانههايش 9 ميباشد. دسترسي به عناصر هر يك از ابعاد آرايه با انديس امكان پذير است. معمولاً به بعد اول آرايه سطر و به بعد دوم آن ستون ميگويد. مثلاً monthArray[3,7] عنصر واقع در سطر 3 و ستون 7 آرايه را مشخص مينمايد.(توجه داشته باشيد كه انديس دهي آرايه از صفر آغاز ميشود و بعنوان مثال intArray[12] به خانه شماره 12 آرايه اشاره ميكند اما فراموش نكنيد چون انديس آرايه از صفر آغاز ميشود خانه شماره 12 آرايه، سيزدهمين داده شما را در خود جاي ميدهد.)
12
11
10
9
8
7
6
5
4
3
2
1
0
اگر شكل فوق را آرايهاي تك بعدي تصور نماييد، مشاهده مينماييد كه خانه شماره 5 آرايه حاوي اطلاعات مربوط به ششمين داده ورودي شما ميباشد.
نكته ديگري كه بايد در مورد تعريف آرايههاي اين مثال متذكر شوم در مورد آريههائيست كه از نوع string تعريف ميشوند. دوستاني كه با زبان C كار كردهاند حتماً ميدانند كه آرايهاي از نوع رشتهاي در C وجود ندارد و براي نگهداري چندين رشته در يك آرايه بايد از آرايه دو بعدي استفاده كرد. در C# اين قابليت فراهم شده تا با استفاده از يك آرايه تك بعدي بتوان چندين رشته را ذخيره نمود بدين صورت كه هر يك از عناصر آرايه نك بعدي محلي براي ذخيرهسازي يك رشته است و همانند زبان C نياز به پردازشهاي گاه پيچيده بر روي آرايههاي چند بعدي بمنظور كار با رشتهها، وجود ندارد. بعنوان يك توضيح كمي اختصاصي عرض ميكنم كه در زبانهايي مانند C، در صورتيكه ميخواستيد چندين رشته را در آرايهاي ذخيره كنيد تا بتوانيد با انديس به آنها دسترسي داشته باشيد، مجبور به تعريف يك آرايه دو بعدي بوديد كه با استفاده از تنها انديس اول آرايه ميتوانستيد به عناصر رشتهاي آرايه دسترسي پيدا كنيد، اما در C# تنها با استفاده از يك آرايه تك بعدي ميتوان همان كار را انجام داد.
string[] stringArray = {"My name is Meysam", "This is C# Persian Blog"}
…..
Console.WriteLine("{0}",stringArray[0]);
…..
همانطور كه در اين مثال ملاحظه ميكنيد، آرايهاي از نوع رشته تعريف شده و دو عنصر به آن تخصيص داده شده است و در جايي در متن برنامه با استفاده از انديس از اولين عنصر اين آرايه براي نمايش در خروجي استفاده گرديده است. خروجي اين برنامه به شكل زير است :
My name is Meysam
مطلب اين درس در اينجا به پايان ميرسد. در صورتيكه نياز داريد تا در مورد عملگرهاي زبان C# بيشتر توضيح دهم حتماً ذكر كنيد تا در مطلب بعدي توضيح كاملتري در مورد آنها براي شما تهيه كنم. خيلي دوست داشتم كه در مورد تمام عملگرهاي زبان C# در همين درس توضيح بدهم اما هم فرصت اندك است و هم حجم مطلب اين قسمت زياد ميشد و هم اينكه فكر كردم احتمالاً دوستان با اين عملگرها آشنايي دارند. نكته ديگري كه بايد به آن اشاره كنم و اينست كه در اين سايت سعي شده است تا زبان برنامهنويسي C# به سادگي و به سرعت آموزش داده شود و علت اينكه به برخي از جزئيات تخصصي پرداخته نميشود نيز همين مطلب ميباشد. در آينده در مورد آرايهها بيشتر صحبت ميكنم چون عناصر مفيد و سودمندي هستند. اميد است پس از كامل كردن مطالب مقدماتي در اين سايت و با همكاري شما عزيزان بتوانم به مطالب پيشرفتهتري از زبان C# بپردازم. بيان نظرات و پيشنهادات شما چه در زمينه مطالب ارائه شده و چه در زمينه متن ارائه شده به شما از لحاظ سادگي و رواني در درك، مرا در امر بهبود مطالب ياري مينمايد.
منابع مورد استفاده در اين مطلب :
C# Tutorial For Beginners
Author : Joe Mayo
Copyright © 2001-2003
www.csharp-station.com (http://pnu-club.com/redirector.php?url=http%3A%2F%2Fwww.csharp-station.com%2F)
C# Unleashed
By : Joe Mayo
Publication : SAMS 2002
C# Reference
By : Microsoft C# Team
Publication : Microsoft Press 2001
درس سوم – دستورالعملهاي كنترلي و شرطي
در اين درس با دستورالعملهاي كنترل و انتخاب در C# آشنا ميشويد. هدف اين درس عبارتست از :
يادگيري دستور if
يادگيري دستور switch
نحوه بكارگيري دستور break در دستور switch
درك صحيح از نحوه بكارگيري دستور goto
بررسي دستور if و انواع مختلف آن
در درسهاي گذشته، برنامههايي كه مشاهده ميكرديد از چندين خط دستور تشكيل شده بودند كه يكي پس از ديگري اجرا ميشدند و سپس برنامه خاتمه مييافت. در اين برنامهها هيچ عمل تصميمگيري صورت نميگرفت و تنها دستورات برنامه به ترتيب اجرا ميشدند. مطالب اين درس نحوه تصميمگيري در يك برنامه را به شما نشان ميدهد.
اولين دستور تصميمگيري كه ما آنرا بررسي مينماييم، دستورالعمل if است. اين دستور داراي سه فرم كلي : تصميمگيري ساده، تصميمگيري دوگانه، تصميمگيري چندگانه ميباشد.
مثال 1-3 – فرمهاي دستورالعمل if
using System;
class IfSelect
{
public static void Main()
{
string myInput;
int myInt;
Console.Write("Please enter a number: ");
myInput = Console.ReadLine();
myInt = Int32.Parse(myInput);
//تصميمگيري ساده و اجراي عمل داخل دو كروشه
if (myInt > 0)
{
Console.WriteLine("Your number {0} is greater than zero.", myInt);
}
//تصميمگيري ساده و اجراي عمل بدون استفاده از دو كروشه
if (myInt < 0)
Console.WriteLine("Your number {0} is less than zero.", myInt);
// تصميمگيري دوگانه
if (myInt != 0)
{
Console.WriteLine("Your number {0} is not equal to zero.", myInt);
}
else
{
Console.WriteLine("Your number {0} is equal to zero.", myInt);
}
// تصميمگيري چندگانه
if (myInt < 0 || myInt == 0)
{
Console.WriteLine("Your number {0} is less than or equal to zero.", myInt);
}
else if (myInt > 0 && myInt <= 10)
{
Console.WriteLine("Your number {0} is between 1 and 10.", myInt);
}
else if (myInt > 10 && myInt <= 20)
{
Console.WriteLine("Your number {0} is between 11 and 20.", myInt);
}
else if (myInt > 20 && myInt <= 30)
{
Console.WriteLine("Your number {0} is between 21 and 30.", myInt);
}
else
{
Console.WriteLine("Your number {0} is greater than 30.", myInt);
}
} //Main()پايان متد
} //IfSelectپايان كلاس
برنامه 1-3 از يك متغير myInt براي دريافت ورودي از كاربر استفاده مينمايد، سپس با استفاده از يك سري دستورات كنترلي، كه همان دستور if در اينجاست، عمليات خاصي را بسته به نوع ورودي انجام ميدهد. در ابتداي اين برنامه عبارت Please enter a umber: در خروجي چاپ ميشود. دستور Console.ReadLine() منتظر ميماند تا كاربر ورودي وارد كرده و سپس كليد Enter را فشار دهد. همانطور كه در قبل نيز اشاره كردهايم، دستور Console.ReadLine() عبارت ورودي را به فرم رشته دريافت مينمايد پس مقدار ورودي كاربر در اينجا كه يك عدد است به فرم رشتهاي در متغير myInput كه از نوع رشتهاي تعريف شده است قرار ميگيرد. اما ميدانيم كه براي اجراي محاسبات و يا تصميمگيري بر روي اعداد نميتوان از آنها در فرم رشتهاي استفاده كرد و بايد آنها را بصورت عددي مورد استفاده قرار داد. به همين منظور بايد متغير myInput را به نحوي به مقدار عددي تبديل نماييم. براي اين منظور از عبارت Int32.Parse() استفاده مينماييم. اين دستور مقدار رشتهاي متغير داخل پرانتزش را به مقدار عددي تبديل كرده و آنرا به متغير ديگري از نوع عددي تخصيص ميدهد. در اين مثال نيز همانطور كه ديده ميشود، myInput كه تز نوع رشتهاي است در داخل پرانتز قرار گرفته و اين مقدار برابر با myInt كه از نوع int است قرار گرفته است. با اين كار مقدار عددي رشته ورودي كاربر به متغير myInt تخصيص داده ميشود. (توضيح كاملتري در مورد Int32 و ساير تبديلات مشابه به آن در درسهاي آينده و در قسمت نوعهاي پيشرفته مورد بررسي قرار ميگيرند.)حال ما متغيري از نوع مورد نظر در دست داريم و ميتوانيم با استفاده از دستور if بر روي آن پردازش انجام داده و تصميمگيري نماييم.
دستور if
اولين دستور بصورت if (boolean expression) {statements} آورده شده است. دستور if با استفاده از كلمه كليدي if آغاز ميشود. سپس يك عبارت منطقي درون يك زوج پرانتز قرار ميگيرد . پس از بررسي اين عبارات منطقي دستورالعمل/دستورالعملهاي داخل كروشه اجرا ميشوند. همانطور كه مشاهده مينماييد، دستور if يك عبارت منطقي را بررسي ميكند. در صورتيكه مقدار اين عبارات true باشد دستورهاي داخل بلوك خود را اجرا مينمايد(قبلا توضيح داده شد كه دستورهايي كه داخل يك زوج كروشه {} قرار ميگيرند در اصطلاح يك بلوك ناميده ميشوند.) و در صورتيكه مقدار آن برابر با false باشد اجراي برنامه به بعد از بلوك if منتقل ميشود. در اين مثال همانطور كه ملاحظه مينماييد، عبارت منطقي دستور if بشكل if(myInt > 0) است. در صورتيكه مقدار myInt بزرگتر از عدد صفر باشد، دستور داخل بلوك if اجرا ميشود و در غير اينصورت اجراي برنامه به بعد از بلوك if منتقل ميگردد.
دومين دستور if دراين برنامه بسيار شبيه به دستور اول است، با اين تفاوت كه در اين دستور، دستور اجرايي if درون يك بلوك قرار نگرفته است. در صورتيكه بخواهيم با استفاده از دستور if تنها يك دستورالعمل اجرا شود، نيازي به استفاده از بلوك براي آن دستورالعمل نميباشد. استفاده از بلوك تنها زماني ضروري است كه بخواهيم از چندين دستور استفاده نماييم.
دستور if-else
در بيشتر موارد از تصميمگيريهاي دوگانه يا چندگانه استفاده ميشود. در اين نوع تصميمگيريها، دو يا چند شرط مختلف بررسي ميشوند و در صورت true بودن يكي از آنها عمل مربوط به آن اجرا ميگردد. سومين دستور if در اين برنامه نشان دهنده يك تصميمگيري دوگانه است. در اين حالت درصورتيكه عبارت منطقي دستور if برابر با true باشد دستور بعد از if اجرا ميشود و در غير اينصورت دستور بعد از else به اجرا در ميآيد. در حقيقت در اين حالت ميگوئيم " اگر شرط if صحيح است دستورات مربوط به if را انجام بده و درغير اينصورت دستورات else را اجرا كن".
فرم كلي دستور if-else بصورت زير است :
if (boolean expression)
{statements}
else
{statements}
كه در آن boolean expression عبارت منطقي است كه صحت آن مورد بررسي قرار ميگيرد و statements دستور يا دستوراتي است كه اجرا ميگردند.
دستور if-else if … else يا if تودرتو
در صورتيكه نياز باشد تا چندين حالت منطقي مورد بررسي قرار گيرد و دستورات مربوط به يكي از آنها اجرا شود، از فرم تصميمگيري چندگانه استفاده مينماييم. اين نوع استفاده از دستور if در اصطلاح به if تودرتو (Nested If) معروف است چراكه در آن از چندين دستور if مرتبط به يكديگر استفاده شده است. چهارمين دستور if در مثال 1-3 استفاده از if تودرتو را نشان ميدهد. در اين حالت نيز دستور با كلمه كليدي if آغاز ميگردد. شرطي بررسي شده و در صورت true بودن دستورات مربوط به آن اجرا ميگردد. اما اگر مقدار اين عبارت منطقي false بود آنگاه شرطهاي فرعي ديگري بررسي ميشوند.اين شرطهاي فرعي با استفاده از else if مورد بررسي قرار ميگيرند. هر يك از اين شرطها داراي عبارات منطقي مربوط به خود هستند كه در صورت true بودن عبارت منطقي دستورات مربوط به آنها اجرا ميگردد و در غير اينصورت شرط بعدي مورد بررسي قرار ميگيرد. بايد توجه كنيد كه در ساختار if تودرتو تنها يكي از حالتها اتفاق ميافتد و تنها يكي از شرطها مقدار true را بازميگرداند.
فرم كلي if تودرتو بشكل زير است :
if (boolean expression)
{statements}
else if (boolean expression)
{statements}
…
else
{statements}
عملگرهاي OR و AND (|| و &&)
نكته ديگري كه بايد در اينجا بدان اشاره كرد، نوع شرطي است كه در عبارت منطقي دستور if آخر مورد استفاده قرار گرفته است. در اين عبارت منطقي از عملگر || استفاده شده است كه بيانگر OR منطقي است. عملگر OR زماني مقدار true بازميگرداند كه حداقل يكي از عملوندهاي آن داراي مقدار true باشد. بعنوان مثال در عبارت (myInt < 0 || myInt == 0)، در صورتيكه مقدار متغير myInt كوچكتر يا مساوي با صفر باشد، مقدار عبارت برابر با true است. نكته قابل توجه آنست كه در زبان C#، همانطور كه در درس دوم به آن اشاره شد، دو نوع عملگر OR وجود دارد. يكي OR منطقي كه با || نمايش داده ميشود و ديگري OR معمولي كه با | نشان داده ميشود. تفاوت بين اين دو نوع OR در آنست كه OR معمولي هر دو عملگر خود را بررسي مينمايد اما OR منطقي تنها در صورتيكه عملگر اول آن مقدار false داشته باشد به بررسي عملگر دوم خود ميپردازد.
عبارت منطقي (myInt > 0 && myInt <= 10) حاوي عملگر AND شرطي (&&) ميباشد. اين عبارت در صورتي مقدار true بازميگرداند كه هر دو عملوند AND داراي مقدار true باشند. يعني در صورتيكه myInt هم بزرگتر از صفر باشد و هم كوچگتر از 10، مقدار عبارت برابر با true ميگردد. در مورد AND نيز همانند OR دو نوع عملگر وجود دارد. يكي AND معمولي (&) و ديگري AND شرطي (&&). تفاوت اين دو نيز در آنست كه AND معمولي (&) هميشه هر دو عملوند خود را بررسي مينمايد ولي AND شرطي (&&) تنها هنگامي به بررسي عملوند دوم خود ميپردازد كه مقدار اولين عملوندش برابر با true باشد. عملگرهاي منطقي (|| و &&) را در اصطلاح عملگرهاي ميانبر (short-circuit) مينامند چراكه تنها در صورت لزوم عملوند دوم خود را بررسي مينمايند و از اينرو سريعتر اجرا ميشوند.
بررسي دستور switch
همانند دستور if، دستور switch نيز امكان تصميمگيري را در يك برنامه فراهم مينمايد.
مثال 2-3 – دستورالعمل switch
using System;
class SwitchSelect
{
public static void Main()
{
string myInput;
int myInt;
begin:
Console.Write("Please enter a number between 1 and 3: ");
myInput = Console.ReadLine();
myInt = Int32.Parse(myInput);
// بهمراه متغيري از نوع صحيح switch دستور
switch (myInt)
{
case 1:
Console.WriteLine("Your number is {0}.", myInt);
break;
case 2:
Console.WriteLine("Your number is {0}.", myInt);
break;
case 3:
Console.WriteLine("Your number is {0}.", myInt);
break;
default:
Console.WriteLine("Your number {0} is not between 1 and 3.", myInt);
break;
} //switchپايان بلوك
decide:
Console.Write("Type \"continue\" to go on or \"quit\" to stop: ");
myInput = Console.ReadLine();
// بهمراه متغيري از نوع رشتهاي switch دستور
switch (myInput)
{
case "continue":
goto begin;
case "quit":
Console.WriteLine("Bye.");
break;
default:
Console.WriteLine("Your input {0} is incorrect.", myInput);
goto decide;
} //switchپايان بلوك
} //Main()پايان متد
} //SwitchSelectپايان كلاس
مثال 2-3 دو مورد استفاده از دستور switch را نشان ميدهد. دستور switch بوسيله كلمه كليدي switch آغاز شده و به دنبال آن عبارت دستور switch قرار ميگيرد. عبارت دستور switch ميتواند يكي از انواع زير باشد : sbyte, byte, short, ushort, int, uint, long, ulong, char, string, enum .(نوع enum در مبحث جداگانهاي مورد بررسي قرار خواهد گرفت.) در اولين دستور switch در مثال 2-3، عبارت دستور switch از نوع عددي صحيح (int) ميباشد.
به دنبال دستور و عبارت switch، بلوك switch قرار ميگيرد كه در آن گزينههايي قرار دارند كه جهت منطبق بودن با مقدار عبارت switch مورد بررسي قرار ميگيرند. هر يك از اين گزينهها با استفاده از كلمه كليدي case مشخص ميشوند. پس از كلمه كليدي case خود گزينه قرار ميگيرد و به دنبال آن ":" و سپس دستوري كه بايد اجرا شود. بعنوان مثال به اولين دستور switch در اين برنامه توجه نماييد. در اينجا عبارت دستور switch از نوع int است. هدف از استفاده از دستور switch آنست كه از بين گزينههاي موجود در بلوك switch، گزينهاي را كه مقدارش با مقدار عبارت switch برابر است پيدا شده و عمل مرتبط با آن گزينه اجرا شود. در اين مثال مقدار متغير myInt بررسي ميشود. سپس اگر اين مقدار با يكي از مقادير گزينههاي داخل بلوك switch برابر بود، دستور يا عمل مربوط به آن گزينه اجرا ميگردد. توجه نماييد كه در اين مثال منظور ما از گزينه همان عدد پس از case است و منظور از دستور عبارتي است كه پس از ":" قرار گرفته است. بعنوان مثال، در دستور زير :
case 1:
Console.WriteLine("Your number is {0}.", myInt);
عدد 1، گزينه مورد نظر ما و دستور Console.WriteLine(…)، عمل مورد نظر است. در صورتيكه مقدار myInt برابر با عدد 1 باشد آنگاه دستور مربوط به case 1 اجرا ميشود كه همان Console.WriteLine("Your number is {0}.", myInt); است. پس از منطبق شدن مقدار عبارت switch با يكي از case ها، بلوك switch بايد خاتمه يابد كه اين عمل بوسيله استفاده از كلمه كليدي break، اجراي برنامه را به اولين خط بعد از بلوك switch منتقل مينمايد.
همانطور كه در ساختار دستور switch مشاهده مينماييد، علاوه بر case و break، دستور ديگري نيز در داخل بلوك وجود دارد. اين دستور يعني default، براي زماني مورد استفاده قرار ميگيرد كه هيچ يك از گزينههاي بلوك switch با عبارت دستور switch منطبق نباشند. به عبارت ديگر درصورتيكه مقدار عبارت switch با هيچ يك از گزينههاي case برابر نباشد، دستور مربوط به default اجرا ميگردد. استفاده از اين دستور در ساختار بلوك switch اختياري است. همچنين قرار دادن دستور break پس از دستور default نيز اختياري ميباشد.
همانطور كه قبلاً نيز گفته شد پس از هر دستور case، به منظور خاتمه دادن اجراي بلوك switch بايد از يك break استفاده نمود. دو استثنا براي اين موضوع وجود دارد. اول اينكه دو دستور case بدون وجود كد و دستورالعملي در بين آنها، پشت سر هم قرار گيرند و ديگري در زمانيكه از دستور goto استفاده شده باشد.
در صورتيكه دو دستور case بدون وجود كدي در بين آنها، پشت سر يكديگر قرار گيرند، بدين معناست كه براي هر دو case مورد نظر يك عمل خاص در نظر گرفته شده است. به مثال زير توجه نماييد.
switch (myInt)
{
case 1:
case 2:
case 3:
Console.WriteLine("Your number is {0}.", myInt);
break;
default:
Console.WriteLine("Your number {0} is not between 1 and 3.", myInt);
break;
}
در اين مثال، همانطور كه مشاهده ميكنيد، سه دستور case بدون وجود كدي در بين آنها پشت سر يكديگر قرار گرفتهاند. اين عمل بدين معناست كه براي تمامي گزينههاي 1، 2 و 3 دستور ;(Console.WriteLine("Your number is {0}.", myInt اجرا خواهد شد. يعني اگر مقدار myInt برابر با هر يك از مقادير 1، 2 و 3 باشد، يك دستور براي آن اجرا ميشود.
نكته قابل توجه ديگر در مورد بلوك switch آنست كه، دستورات case حتماً نبايد يك دستور باشد بلكه ميتوان از يك بلوك دستور براي case استفاده نمود.
دومين استفاده از دستور switch در مثال 2-3، داراي عبارتي از نوع رشتهايست. در اين بلوك switch چگونگي استفاده از دستور goto نيز نشان داده شده است. دستور goto اجراي برنامه را به برچسبي (label) كه معين شده هدايت مينمايد. در حين اجراي اين برنامه، اگر كاربر رشته continue وارد نمايد، اين رشته با يكي از گزينههاي دومين switch منطبق ميشود. چون دستور case مربوط به اين گزينه داراي دستور goto است، اجراي برنامه به برچسبي كه اين دستور مشخص كرده فرستاده ميشود، بدين معني كه اجراي برنامه به ابتداي جايي ميرود كه عبارت begin: در آنجا قرار دارد (در اوايل متد Main()). بدين صورت اجراي برنامه از بلوك switch خارج شده و به ابتداي برنامه و در جائيكه برچسب begin: قرار گرفته ارسال ميشود. در اين برنامه، استفاده از چنين حالتي استفاده از goto باعث ايجاد يك حلقه شده است كه با وارد كردن عبارت quit اجراي آن به پايان ميرسد.
در صورتيكه هيچ يك از عبارات continue و يا quit وارد نشوند، اجراي switch به گزينه default ميرود و در اين گزينه ابتدا پيغام خطايي بر كنسول چاپ شده و سپس با استفاده از دستور goto پرشي به برچسب decide صورت ميگيرد. پس از پرش به برچسب decide، از كاربر پرسيده ميشود كه آيا ميخواهد اجراي برنامه را ادامه دهد يا خير.( با وارد كردن گزينههاي continue يا quit) همانطور كه ميبينيد در اينجا نيز حلقهاي توليد شده است.
استفاده از دستور goto در بلوك switch ميتواند موثر باشد اما بايد توجه نماييد كه استفادههاي بي مورد از دستور goto باعث ناخوانا شدن برنامه شده و عيبيابي (Debug) برنامه را بسيار دشوار مينمايد. در برنامهنويسيهاي امروزي استفاده از دستور goto بغير از موارد بسيار لازم و ضروري منسوخ شده و به هيچ عنوان توصيه نميشود. براي توليد و ساخت حلقه نيز دستورات مفيد و سودمندي در زبان تعبيه شدهاند كه استفاده از goto را به حداقل ميرسانند. دستورات حلقه در مبحث آينده مورد بررسي قرار خواهند گرفت.
نكته پاياني اين مبحث آنست كه توجه نماييد كه به جاي استفاده از دستور switch ميتوانيد از چندين دستور if-else استفاده نمايد. دو قطعه برنامه زير معادل يكديگر ميباشند.
switch(myChar)
{
case 'A' :
Console.WriteLine("Add operation\n");
break;
case 'M' :
Console.WriteLine("Multiple operation\n");
break;
case 'S' :
Console.WriteLine("Subtraction operation\n");
break;
default :
Console.WriteLine("Error, Unknown operation\n");
break;
}
معادل بلوك switch با استفاده از if-else
if (myChar == 'A')
Console.WriteLine("Add operation\n");
else if (myChar == 'M')
Console.WriteLine("Multiple operation\n");
else if (myChar == 'S')
Console.WriteLine("Subtraction operation\n");
else
Console.WriteLine("Error, Unknown operation\n");
همانطور كه ملاحظه ميكنيد استفاده از بلوك دستور switch بسيار سادهتر از استفاده از if-else هاي تودرتو است.
در اين درس با نحوه تصميمگيري در برنامه بوسيله دستور if و switch آشنا شديد. با نحوه عملكرد و استفاده دستور goto نيز آشنايي پيدا كرديد. در پايان مجدداً يادآوري ميكنم كه در استفاده از دستور goto با احتياط عمل نماييد و به جز در موارد ضروري از آن استفاده نكنيد.
درس چهارم – دستورالعملهاي كنترلي، حلقهها
در اين درس نحوه استفاده از دستورالعملهاي كنترل حلقه در زبان C# را فرا خواهيد گرفت. هدف اين درس فهم و درك موارد زير ميباشد :
ü حلقه while
ü حلقه do-while
ü حلقه for
ü حلقه foreach
ü مطالب تكميلي درباره دستورالعمل break
ü فراگيري نحوه بكارگيري دستورالعمل continue
در درس قبل، نحوه ايجاد يك حلقه بسيار ساده را با استفاده از دستور goto را فرا گرفتيد. در همان مطلب نيز اشاره كرديم كه اين روش، روش مناسبي جهت ايجاد حلقه نيست. در اين درس با نحوه صحيح ايجاد حلقهها در زبان C# آشنا خواهيد شد. اولين دستوري كه با آن آشنا ميشويد نيز دستور while است.
حلقه while
ابتدا به مثال زير توجه نماييد.
using System;
class WhileLoop
{
public static void Main()
{
int myInt = 0;
while (myInt < 10)
{
Console.Write("{0} ", myInt);
myInt++;
}
Console.WriteLine();
}
}
مثال 1-4 كه در بالا ملاحظه ميكنيد، يك حلقه while ساده را نشان ميدهد. اين حلقه با كلمه كليدي while آغاز شده و سپس به دنبال آن يك عبارت منطقي قرار ميگيرد و مورد بررسي قرار ميگيرد. تمامي دستورالعملهاي كنترلي از يك عبارت منطقي بهره ميگيرند و اين بدين معناست كه ابتدا اين عبارت بايد بررسي شود تا مشخص شود مقدار اين عبارت true است يا false. در اين مثال مقدار متغير myInt مورد بررسي قرار ميگيرد تا چك شود آيا مقدارش از 10 كوچكتر هست يا خير. چون در ابتداي برنامه به اين متغير مقدار صفر تخصيص داده شده است، عبارت منطقي مقدار true را باز ميگرداند و سپس بلوك قرار گرفته بعد از عبارت منطقي مورد اجرا قرار ميگيرد.
درون بلوك while ابتدا مقدار متغير myInt در كنسول نمايش داده ميشود و سپس يك واحد به مقدار اين متغير افزوده ميگردد. پس از اتمام بلوك while، عبارت منطقي مجددا كنترل ميشود و در صورتيكه اين عبارت مقدار true بازگرداند، حلقه while مجدداً اجرا ميشود. زمانيكه عبارت منطقي مقدار false برگرداند، اجرا برنامه به اولين دستور بعد از بلوك while منتقل ميشود. در اين مثال اعداد صفر تا 9 بر روي صفحه نمايش داده ميشوند و سپس يك خط خالي چاپ شده و اجراي برنامه خاتمه مييابد.
حلقه بعدي كه بسيار شبيه به حلقه while ميباشد، حلقه do-while است.
حلقه do-while
ابتدا به مثال 2-4 توجه نماييد.
using System;
class DoLoop
{
public static void Main()
{
string myChoice;
do
{
// منويي نمايش داده ميشود
Console.WriteLine("My Address Book\n");
Console.WriteLine("A - Add New Address");
Console.WriteLine("D - Delete Address");
Console.WriteLine("M - Modify Address");
Console.WriteLine("V - View Addresses");
Console.WriteLine("Q - Quit\n");
Console.WriteLine("Choice (A,D,M,V,or Q): ");
// ورودي كاربر بررسي ميشود
myChoice = Console.ReadLine();
// تصميمي بر اساس ورودي كاربر گرفته ميشود
switch(myChoice)
{
case "A":
case "a":
Console.WriteLine("You wish to add an address.");
break;
case "D":
case "d":
Console.WriteLine("You wish to delete an address.");
break;
case "M":
case "m":
Console.WriteLine("You wish to modify an address.");
break;
case "V":
case "v":
Console.WriteLine("You wish to view the address list.");
break;
case "Q":
case "q":
Console.WriteLine("Bye.");
break;
default:
Console.WriteLine("{0} is not a valid choice", myChoice);
break;
}
Console.Write("Press Enter key to continue...");
Console.ReadLine();
Console.WriteLine();
} while (myChoice != "Q" && myChoice != "q");
}
}
مثال 2-4 نحوه استفاده از حلقه do-while را نشان ميدهد. ساختار نوشتاري اين دستور بصورت زير است :
do
{ <statements> } while (<boolean expression>);
دستورالعملهاي مورد استفاده در بلوك اين دستور، هر دستورالعمل معتبر زبان C# ميتواند باشد. عبارت منطقي نيز همانند نمونههائيست كه تا كنون با آنها آشنا شديم و يكي از دو مقدار true يا false را بر ميگرداند.
يكي از مصارف عمده حلقه do به جاي حلقه while، مواردي است كه ميخواهيم يكسري دستورالعمل خاص، كه آنها را درون بلوك do قرار ميدهيم، حداقل يكبار اجرا شوند. در اين مثال ابتدا يك منو براي كاربر نمايش داده ميشود و سپس ورودي از وي دريافت ميگردد. چون حلقه while عبارت منطقي خود در ابتداي اجراي حلقه بررسي مينمايد، از اينرو تضميني براي اجراي دستورات درون بلوك وجود نخواهد داشت، مگر شما بطور صريح برنامه را طوري طراحي نماييد كه اين عمل اتفاق بيفتد.
يك نگاه كلي به مثال 2-4 بيندازيم. در متد Main() متغير myChoice را از نوع رشتهاي تعريف نمودهايم. سپس يكسري دستورات را بر روي كنسول چاپ نمودهايم. اين دستورات منوهاي انتخاب براي كاربر هستند. ما بايد ورودي از كاربر دريافت كنيم كه چون اين عمل از طريق Console.ReadLine() صورت گرفته، بايد در متغيري از نوع رشتهاي قرار گيرد و از اينرو اين ورودي را در myChoice قرار دادهايم. ما بايد ورودي را از كاربر دريافت كنيم و بر روي آن پردازش انجام دهيم. يك روش كارآمد براي اين منظور استفاده از دستورالعمل switch است. همانطور كه در دستور switch ملاحظه ميكنيد، بري default نيز دستوري در نظر گرفته شده است كه نشان ميدهد مقدار ورودي معتبر نيست.
حلقه for
به مثال 3-4 توجه كنيد.
using System;
class ForLoop
{
public static void Main()
{
for (int i=0; i < 20; i++)
{
if (i == 10)
break;
if (i % 2 == 0)
continue;
Console.Write("{0} ", i);
}
Console.WriteLine();
}
}
مثال 3-4 يك حلقه for را نشان ميدهد. استفاده از حلقه for براي زماني مناسب است كه دقيقاً بدانيد كه حلقه چندبار بايد تكرا شود. محتويات درون پرانتزهاي حلقه for از سه قسمت تشكيل شده است :
(<initializer list>; <boolean expression>; <postloopaction list>)
initializer list ليستي از عبارات است كه بوسيله كاما از يكديگر جدا ميشوند. اين عبارات تنها يكبار در طول دوره كاري حلقه for پردازش ميشوند. همانطور كه در مثال 3-4 نيز ملاحظه ميكنيد، اين قسمت معمولا براي تعيين متغيري عددي جهت آغاز عمل شمارش مورد استفاده قرار ميگيرد.
پس از اينكه عبارتهاي دورن initializer list پردازش شد، حلقه for به سراغ قسمت بعدي، يعني عبارات منطقي(boolean expression) ميرود. در اين قسمت تنها يك عبارت منطقي ميتوان قرار داد ولي هر اندازه كه بخواهيد ميتوانيد اين عبارت منطقي را پيچيده نماييد، فقط توجه نماييد كه اين عبارت بايد بگونهاي شود كه مقدار true يا false برگرداند. از اين عبارت منطقي معمولا جهت كنترل متغير شمارشي استفاده ميشود.
هنگاميكه عبارت منطقي مقدار true بازگرداند، دستورالعملهاي بلوك for اجرا ميشوند. در مثال 3-4 ما از دو دستور if درون حلقه for نيز استفاده كردهايم. اولين دستور if بررسي ميكند كه آيا مقدار متغير i برابر با 10 هست يا نه. در اينجا يك نمونه ديگر از استفاده دستور break را ملاحظه ميكنيد. عملكرد دستور break در اينجا نيز همانند مورد استفاده آن در دستور switch است. در صورت اجراي دستور break اجراي حلقه for خاتمه يافته و اجراي برنامه به اولين دستور بعد از حلقه for منتقل ميشود.
دومين دستور if با اسقتاده از عملگر باقيمانده (%) بررسي ميكند كه آيا متغير i بر 2 بخش پذير هست يا نه. در صورتيكه متغير i بر 2 بخش پذير باشد، دستور continue اجرا ميشود. پس از اجراي دستور continue از ساير دستورات حلقه for كه بعد از continue قرار گرفتهاند صرفهنظر ميشود و اجراي برنامه به اول حلقه for باز ميگردد.
قسمت سوم در حلقه for، قسمت postloopaction list است. پس از اينكه تمامي دستورات درون حلقه for اجرا شد، اجراي حلقه به اين قسمت باز ميگردد. اين قسمت ليستي از عملياتي است كه ميخواهيد پس از اجراي دستورات درون بلوك حلقه for انجام شوند. در مثال 3-4 اين عمل، اضافه كردن يك واحد به متغير شمارشي است. پس از افزوده شدن يك واحد به متغير شمارشي، عبارت منطقي مجدداً مورد بررسي قرار ميگيرد و در صورتيكه مقدار اين عبارت برابر با true باشد، حلقه for مجدداً اجرا ميگردد. حلقه for تا زمانيكه عبارت منطقي برابر با true باشد اجرا ميشود.
حلقه foreach
به مثال 4-4 توجه كنيد.
using System;
class ForEachLoop
{
public static void Main()
{
string[] names = {"Meysam", "Ghazvini", "C#", "Persian"};
foreach (string person in names)
{
Console.WriteLine("{0} ", person);
}
}
}
حلقه foreach براي پيمايش مجموعهها بسيار مناسب است. يك نمونه از مجموعهها در C#، آرايهها هستند كه در مثال 4-4 نيز مورد استفاده قرار گرفته است. اولين كاري كه در متد Main() صورت گرفته، اعلان آرايه names از نوع رشتهاي و مقدار دهي آن است.
درون پرانتزهاي foreach عبارتي متشكل از دو المان قرار دارد كه اين المانها بوسيله كلمه كليدي in از يكديگر جدا شدهاند. المان سمت راست، مجموعهايست كه ميخواهيد اعضاي آنرا مورد پيمايش قرار دهيد. المان سمت چپ، متغيري از نوع مجموعه مورد نظر است كه مقادير پيمايش شده را بر ميگرداند.
در هربار پيمايش، عنصري جديدي از مجموعه درخواست ميشود. اين درخواستها از طريق متغير فقط خواندني تعريف شده درون پرانتزهاي foreach بازگردانده ميشوند. تا زمانيكه عنصري در مجموعه وجود داشته باشد كه مورد پيمايش قرار نگرفته است، حلقه foreach به كار خود ادامه خواهد داد زيرا عبارت منطقي حلقه foreach مقدار true را باز ميگرداند. به محض اينكه تمامي عناصر مجموعه پيمايش شد، عبارت منطقي برابر با false شده و اجراي حلقه foreach خاتمه مييابد. در اين حالت اجراي برنامه به اولين دستور بعد از حلقه foreach منتقل ميگردد.
در مثال 4-4، متغيري از نوع رشته با نام person براي نگهداري عناصري كه از آرايه names خوانده ميشود، تعريف كردهايم. تا زمانيكه اسمي در آرايه names وجود داشته باشد، در متغير person قرار ميگيرد و درون حلقه foreach بوسيله دستور Console.WriteLine() در خروجي نمايش داده ميشود.
نكته : يكي از مهمترين ويژگيهاي حلقه foreach در آنست كه فقط ميتواند عناصر يك مجموعه را بخواند و نميتواند تغييري در آنها ايجاد نمايد. مزيت ديگر آن، پيمايش تمامي عناصر مجموعه بدون اطلاع از تعداد عناصر موجود در آن است.
خلاصه
در اين درس با نحوه كار با دستورالعملهاي for، while، do-while و foreach آشنا شديد. حلقه while تا زمانيكه شرطش صحيح باشد انجام ميشود بدين معني كه تا زمانيكه عبارت منطقي آن داراي مقدار true باشد، دستورات درون بلوك آن اجرا ميشوند. حلقه do، دستورات بلوك خود را حداقل يكبار اجرا ميكند و سپس شرط مورد نظر را بررسي مينمايد و در صورتيكه عبارت منطقي آن مقدار true بازگرداند، دستورات بلوك خود را تكرار مينمايد. حلقه for دستورات بلوك خود را به تعداد دفعات مشخص اجرا مينمايد و حلقه foreach عناصر يك مجموعه را مورد پيمايش قرار ميدهد. در نهايت نيز اشاره ميشود كه روند اجراي حلقهها با استفاده از دستورات break و continue تغيير مينمايد.
درس پنجم – متدها
در اين قسمت با متدها در زبان C# آشنا ميشويد. اهداف اين درس به شرح زير ميباشد :
ü درك ساختار يك متد
ü درك تفاوت بين متدهاي استاتيك (static methods) و متدهاي نمونه (instance)
ü ايجاد نمونه جديد از اشياء
ü نحوه فراخواني متدها
ü درك چهار گونه متفاوت پارامترها
ü نحوه استفاده از مرجع this
تا كنون تمامي اعمالي كه ما در برنامههايمان انجام ميداديم در متد Main() اتفاق ميافتادند. اين روش براي برنامههاي ساده و ابتدايي كه استفاده كرديم مناسب بود، اما اگر برنامهها پيچيدهتر شوند و تعداد كارهاي مورد نظر ما گسترش يابد، استفاده از متدها جايگزين روش قبل ميگردد. متدها فوقالعاده مفيد هستند، چراكه كارها را به بخشهاي كوچكتر و مجزا تقيسم ميكنند و در نتيجه استفاده از آنها آسانتر خواهد بود.
ساختار كلي يك متد به صورت زير است :
[attributes][ modifiers] return-type method-name ([ parameters] ) { statements }
دو قسمت attributes و modifiers را در آينده مورد بررسي قرار خواهيم داد. return-type نوعي است يك متد باز ميگرداند و ميتواند هر يك از انواع تعريف شده زبان C# و يا از انواع تعريف شده توسط كاربر باشد. هر متد با نام آن شناخته ميشود. method-name نام انتخابي برنامهنويس براي يك متد است و از طريق همين نام فراخواني متد انجام ميشود. پارامترها (parameters) مولفهها يا متغيرهايي هستند كه براي انجام يكسري پردازش به متد ارسال ميشوند و از طريق آنها ميتوان اطلاعاتي را به متد ارسال و يا از آن دريافت نمود، و در نهايت دستورالعمهاي متد، دستورهايي از زبان C# هستند كه بوسيله آنها عمل مورد نظر برنامهنويس انجام ميشود و عملي است كه يك متد آنرا انجام ميدهد. مثال 1-5 پيادهسازي يك متد ساده را نمايش ميدهد.
using System;
class OneMethod
{
public static void Main()
{
string myChoice;
OneMethod om = new OneMethod();
do
{
myChoice = om.getChoice();
// تصميمي بر اساس انتخاب كاربر گرفته ميشود
switch(myChoice)
{
case "A":
case "a":
Console.WriteLine("You wish to add an address.");
break;
case "D":
case "d":
Console.WriteLine("You wish to delete an address.");
break;
case "M":
case "m":
Console.WriteLine("You wish to modify an address.");
break;
case "V":
case "v":
Console.WriteLine("You wish to view the address list.");
break;
case "Q":
case "q":
Console.WriteLine("Bye.");
break;
default:
Console.WriteLine("{0} is not a valid choice", myChoice);
break;
}
// اجراي برنامه براي ديدن نتايج موقف ميشود
Console.WriteLine();
Console.Write("Press Enter key to continue...");
Console.ReadLine();
Console.WriteLine();
} while (myChoice != "Q" && myChoice != "q");
// اجراي برنامه تا زمانيكه كاربر بخواهد ادامه مييابد
}
string getChoice()
{
string myChoice;
// منويي را نمايش ميدهد
Console.WriteLine("My Address Book\n");
Console.WriteLine("A - Add New Address");
Console.WriteLine("D - Delete Address");
Console.WriteLine("M - Modify Address");
Console.WriteLine("V - View Addresses");
Console.WriteLine("Q - Quit\n");
Console.Write("Choice (A,D,M,V,or Q): ");
// ورودي دريافتي از كاربر را بررسي ميكند
myChoice = Console.ReadLine();
Console.WriteLine();
return myChoice;
}
}
برنامه مثال 1-5 دقيقا همان برنامه در س 4 است، با اين تفاوت كه در درس چهارم چاپ منو و دريافت ورودي از كاربر در متد Main() صورت ميگرفت در حاليكه در اين مثال، اين اعمال در يك متد مجزا بنام getChoice() صورت ميگيرد. نوع بازگشتي اين متد از نوع رشتهاي است. از اين رشته در دستور switch در متد Main() استفاده ميشود. همانطور كه ملاحظه مينماييد، پرانتزهاي متد getChoice() خالي هستند، يعني اين متد داراي پارامتر نيست، از اينرو هيچ اطلاعاتي به/ از اين متد منتقل نميشود.
درون اين متد، ابتدا متغير myChoice را اعلان نمودهايم. هرچند نام و نوع اين متغير همانند متغير myChoice موجود در متد Main() است، اما اين دو متغير دو متغير كاملاً مجزا از يكديگر ميباشند. هر دو اين متغيرها، متغيرهاي محلي (Local) هستند، از اينرو تنها درون بلوكي كه تعريف شدهاند قابل دسترس ميباشند. به بيان ديگر اين دو متغير از وجود يكديگر اطلاعي ندارند.
متد getChoice() منويي را در كنسول نمايش ميدهد و ورودي انتخابي كاربر را دريافت مينمايد. دستور return دادهها را از طريق متغير myChoice به متد فراخواننده آن، يعني Main()، باز ميگرداند. توجه داشته باشيد كه، نوع متغيري كه توسط دستور return باز گردانده ميشود، بايد دقيقاً همانند نوع بازگشتي متد باشد. در اين مثال نوع بازگشتي، رشته است.
در C# دو گونه متد وجود دارد. يكي متدهاي استاتيك (Static) و ديگري متدهاي نمونه (Instance). متدهايي كه در اعلان خود شامل كلمه كليدي static هستند، از نوع استاتيك هستند، بدين معنا كه هيچ نمونهاي از روي اين متد قابل ايجاد نيست و اين تنها همين نمونه موجود قابل استفاده است. از روي متدهاي استاتيك نميتوان شيء (Object) ايجاد كرد. در صورتيكه در اعلان متد از كلمه كليدي static استفاده نشده باشد، متد بعنوان متد نمونه در نظر گرفته ميشود، بدين معنا كه از روي آن ميتوان نمونه ايجاد كرد و شيء توليد نمود. هر يك از اشياء ايجاد شده از روي اين متدها، تمامي عناصر آن متد را داراي ميباشند.
در اين مثال، چون getChoice() بصورت استاتيك اعلان نشده است، پس بايد براي استفاده از آن شيء جديدي توليد شود. توليد شيء جديد بوسيله OneMethod om = new OneMethod() صورت ميپذيرد. در سمت چپ اين اعلان، مرجع اين شيء جديد، يعني om، قرار دارد كه از نوع OneMethod است. در اينجا توجه به يك نكته بسيار مهم است، om به خودي خود شيء نيست، بلكه ميتواند مرجعي به شياي از نوع OneMethod() را در خود نگهدارد. در سمت راست اين اعلان، تخصيص شيء جديدي از نوع OneMethod() به متغير om صورت گرفته است. كلمه كليدي new عملگري است كه شيء جديدي را در heap ايجاد مينمايد. اتفاقي كه اينجا روي داده اينست كه نمونه جديدي از OneMethod() در heap توليد شده و سپس به مرجع om تخصيص داده ميشود. حال كه نمونهاي از متد OneMethod() را به om تخصيص دادهايم، از طريق om ميتوانيم با اين متد كار نماييم.
متدها، فيلدها و ساير اعضاي يك كلاس از طريق عملگر نقطه "." قابل دسترس هستند. هنگاميكه ميخواهيم متد getChoice() را فراخواني كنيم، بوسيله عملگر نقطه از طريق om به آن دسترسي پيدا مينماييم : om.getChoice() . براي نگهداري مقداري كه getChoice() بر ميگرداند، از عملگر "=" استفاده نمودهايم. رشته بازگشتي از متد getChoice() درون متغير محلي myChoice متد Main() قرار ميگيرد. از اين قسمت، اجراي برنامه همانند قبل است.
پارامترهاي متد
به مثال 2-5 توجه كنيد.
using System;
class Address
{
public string name;
public string address;
}//Addressپايان كلاس
class MethodParams
{
public static void Main()
{
string myChoice;
MethodParams mp = new MethodParams();
do
{
// منويي نمايش داده شده و ورودي از كاربر دريافت ميگردد
myChoice = mp.getChoice();
// تصميمي بر اساس ورودي كاربر گرفته ميشود
mp.makeDecision(myChoice);
// جهت ديدن نتايج توسط كاربر، اجراي برنامه موقتا موقف ميگردد
Console.Write("Press Enter key to continue...");
Console.ReadLine();
Console.WriteLine();
} while (myChoice != "Q" && myChoice != "q");
// اجراي حلقه تا زمانيكه كاربر بخواهد ادامه پيدا مينمايد
}//Mainپايان متد
// نمايش منو و دريافت ورودي از كاربر
string getChoice()
{
string myChoice;
// نمايش منو
Console.WriteLine("My Address Book\n");
Console.WriteLine("A - Add New Address");
Console.WriteLine("D - Delete Address");
Console.WriteLine("M - Modify Address");
Console.WriteLine("V - View Addresses");
Console.WriteLine("Q - Quit\n");
Console.WriteLine("Choice (A,D,M,V,or Q): ");
// دريافت ورودي كاربر
myChoice = Console.ReadLine();
return myChoice;
}//getChoice()پايان متد
// تصميمگيري
void makeDecision(string myChoice)
{
Address addr = new Address();
switch(myChoice)
{
case "A":
case "a":
addr.name = "Meysam";
addr.address = "C# Persian";
this.addAddress(ref addr);
break;
case "D":
case "d":
addr.name = "Ghazvini";
this.deleteAddress(addr.name);
break;
case "M":
case "m":
addr.name = "CSharp";
this.modifyAddress(out addr);
Console.WriteLine("Name is now {0}.", addr.name);
break;
case "V":
case "v":
this.viewAddresses("Meysam", "Ghazvini", "C#", "Persian");
break;
case "Q":
case "q":
Console.WriteLine("Bye.");
break;
default:
Console.WriteLine("{0} is not a valid choice", myChoice);
break;
}
}
// وارد كردن يك آدرس
void addAddress(ref Address addr)
{
Console.WriteLine("Name: {0}, Address: {1} added.", addr.name, addr.address);
}
// حذف يك آدرس
void deleteAddress(string name)
{
Console.WriteLine("You wish to delete {0}'s address.", name);
}
// تغيير يك آدرس
void modifyAddress(out Address addr)
{
//Console.WriteLine("Name: {0}.", addr.name); // خطا رخ ميدهد
addr = new Address();
addr.name = "Meysam";
addr.address = "C# Persian";
}
// نمايش آدرسها
void viewAddresses(params string[] names)
{
foreach (string name in names)
{
Console.WriteLine("Name: {0}", name);
}
}
}
مثال 2-5، نمونه تغيير يافته مثال 1-5 است كه در آن تمامي برنامه ماژولار شده و به متدهاي مختلف تقسيم شده است. در زبان C# چهار گونه پارامتر وجود دارند : ref، out، params و value . بمنظور آشنايي با پارامترها، در مثال 2-5 كلاسي با نام Address با دو فيلد از نوع رشته توليد كردهايم.
درون متد Main()، متد getChoice() را فراخواني كردهايم تا از كاربر ورودي دريافت كنيم و اين ورودي در متغير رشتهاي myChoice قرار ميگيرد. سپس متغير myChoice را بعنوان آرگومان به متد makeDecision() ارسال نمودهايم. در اعلان myDecision()، همانطور كه ملاحظه مينماييد، پارامتر اين متد از نوع رشته و با نام myChoice تعريف شده است. توجه نماييد كه اين متغير نيز محلي است و تنها درون متد makeDecision() قابل استفاده است. هرگاه در اعلان متد، براي پارامترهاي آن هيچ modifier آورده نشود، اين پارامتر بعنوان value در نظر گرفته ميشود. در مورد پارامترهاي مقداري (value parameter) ، اصل مقدار متغير يا پارامتر به پشته (Stack) كپي ميشود. متغيرهايي كه بصورت مقداري بعنوان پارامتر براي يك متد ارسال ميشوند، همگي محلي بوده و تغييرات ايجاد شده بر روي آنها به هيچ وجه تغييري بر روي متغير اصلي ايجاد نمينمايد.
دستور switch در متد makeDecision() براي هر case يك متد را فراخواني مينمايد. فراخواني اين متدها با آنچه در متد Main() ديد مقداري متفاوت است. علاوه بر مرجع mp، در اين فراخوانيها از كلمه كليدي this نيز استفاده شده است. كلمه كليدي this ارجاعي به شيء فعلي دارد.
متد addAddress() پارامتري از نوع ref دارد. وجود چنين پارامتري بدين معناست كه مرجعي از اين پارامتر به متد ارسال ميشود و اين مرجع همچنان به شيء اصلي درون heap نيز اشاره دارد چراكه آدرس شيء مورد نظر به متد كپي ميشود. در مورد پارامترهاي ref، هرگونه تغييري كه بر روي متغير محلي رخ دهد، همان تغيير بر روي متغير اصلي نيز اعمال ميگردد. امكان تغيير مرجع وجود ندارد و تنها شياي كه مورد آدرسدهي واقع شده، ميتواند تغيير پيدا نمايد. پارامترهاي مرجعي (ref) را ميتوان به عنوان عناصر ورودي/خروجي براي متد در نظر گرفت.
پارامترهاي out در مواردي استفاده ميشوند كه ارسال اطلاعات به متد از طريق پارامتر مد نظر نباشد، بلكه ارسال اطلاعات از متد مورد نظر باشد. استفاده از اين پارامترها از اينرو كارآمد هستند كه برنامه مجبور به كپي كردن پارامتر به متد نيست و از حجم سرباره (Overhead) برنامه ميكاهد. در برنامههاي عادي اين مسئله چندان به چشم نميآيد، اما در برنامههاي تحت شبكه كه سرعت ارتباط و انتقال دادهها بسيار مهم است، اين پارامترها ضروري ميشوند.
متد modifyAddress() داراي پارامتري از نوع out است. پارامترهاي out فقط به متد فراخواننده آن بازگشت داده ميشوند. از آنجائيكه اين پارامترها از متد فراخواننده هيچ مقداري دريافت نميكنند و فقط درون متدي كه به عنوان پارامتر به آن ارسال شدهاند قابليت تغيير دارند، از اينرو درون اين متدهايي كه به آنها ارسال ميشوند، قبل از اينكه بتوان از آنها استفاده نمود بايد مقداري به آنها تخصيص داده شود. اولين خط در متد modifyAddress() بصورت توضيحات نوشته شده است. اين خط را از حالت توضيحات خارج كرده و سپس برنامه اجرا كنيد تا ببينيد چه اتفاقي رخ خواهد داد. هنگاميكه اين پارامتر مقدار دهي شود و مقداري را به متد فراخواننده خود بازگرداند، اين مقدار بر روي متغير متد فراخواننده كپي ميگردد. توجه نماييد كه پارامترهاي out ميبايست قبل از دستور return درون متد مقدار دهي شده باشند.
يكي از ويژگيهاي مفيد زبان C#، وجود پارامترهاي params است كه بوسيله آنها ميتوان متدي را اعلان كرد كه تعداد متغيري متغير را به عنوان پارامتر دريافت نمايد. پارامترهاي params حتماً بايد يكي از انواع آرايه تك بعدي و يا آرايه دندانهدار (Jagged Array) باشند. در متد makeDecision() چهار متغير رشتهاي را به متد viewAddresses() ارسال نمودهايم كه اين متد پارامترهاي خود را بصورت params دريافت مينمايد. همانطور كه ملاحظه مينماييد، تعداد متغيرهاي ارسالي به متد ميتواند متغير باشد اما دقت داشته باشيد كه تمامي اين متغيرها در يك آرايه تك بعدي قرار گرفتهاند. درون متد viewAddresses() نيز با استفاده از دستور foreach تمامي عناصر موجود در اين آرايه را نمايش دادهايم. پارامترهاي params فقط متغيرهاي ورودي دريافت مينمايند و تغييرات اعمال شده تنها بر روي متغير محلي تاثير ميگذارد.
خلاصه
در اين درس، با ساختار كلي يك متد آشنا شديد. فرا گرفتيد كه در زبان C# چهار نوع پارامتر براي متدها وجود دارند. پارامترهاي value، ref، out و params . همانطور كه گفته شد حالت پيش فرض براي پارامترها، value است مگر آنكه صريحاً مشخص گردد.
درس ششم – Namespaces
اهداف اين درس به شرح زير ميباشد :
ü آشنايي با Namespace در زبان C#
ü چگونگي استفاده از هدايتگر using (using directive)
ü چگونگي استفاده از هدايتگر alias (alias directive)
ü اعضاي يك Namespace چه هستند؟
اگر به خاطر داشته باشيد، در درس اول، در ابتداي برنامه از using System; استفاده نموديم. با استفاده از اين كد، امكان دسترسي ما به اعضاي Namespace مورد نظر، كه در اينجه System است، فراهم ميشد. پس از مطالعه اين درس، مطالب بسياري درباره هدايتگر using فرا خواهيد گرفت.
Namespace ها، المانهاي زبان C# هستند كه شما را در سازماندهي كردن برنامه، كمك ميكنند. پيادهسازي Namespace ها در كد برنامه بسيار مفيد است چراكه از ايجاد مشكلات مربوط به استفاده مجدد كد، پيشگيري مينمايد.
چگونگي ايجاد Namespace
به مثال زير توجه نماييد.
// Namespace اعلان
using System;
// C# Persian Namespace
namespace csharp-persian
{
// كلاس آغازين برنامه
class NamespaceCSS
{
// آغاز اجراي برنامه
public static void Main()
{
// چاپ خروجي در كنسول
Console.WriteLine("This is the new C# Persian Namespace.");
}
}
}
مثال 1-6 چگونگي ايجاد يك Namespace را نشان ميدهد. در اين مثال ما با قرار دادن كلمه كليدي namespace در جلوي csharp-persian يك Namespace جديد ايجاد نموديم. مثال 2-6، Namespace هاي تودرتو را نشان ميدهد.
// Namespace اعلان
using System;
// C# Persian Namespace
namespace csharp-persian
{
namespace tutorial
{
// كلاس آغازين برنامه
class NamespaceCSS
{
// آغاز اجراي برنامه
public static void Main()
{
// چاپ خروجي در كنسول
Console.WriteLine("This is the new C# Persian Namespace.");
}
}
}
}
Namespace ها اين امكان را فراهم ميآورند تا سيستمي جهت سازماندهي كدهاي خود ايجاد نماييد. يك روش مناسب جهت سازماندهي Namespace هاي برنامه، استفاده از يك سيستم سلسله مراتبي است. بدين ترتيب كه Namespace هاي عمومي را در بالاي اين ساختار قرار داده و هر چه در اين ساختار پاينتر ميآييم، Namespace هاي تخصصيتر قرار ميگيرند. اين سيستم سلسله مراتبي بوسيله Namespace هاي تودرتو قابل پيادهسازي هستند. اين پيادهسازي در مثال 2-6 نشان داده شده است. با قرار دادند كدهاي خود در Namespace هاي فرعي ميتوانيد كدهاي خود را سازماندهي نماييد.
// Namespace اعلان
using System;
// C# Persian Tutorial Namespace
namespace csharp-persian.tutorial
{
// كلاس آغازين برنامه
class NamespaceCSS
{
//آغاز اجراي برنامه
public static void Main()
{
// چاپ خروجي در كنسول
Console.WriteLine("This is the new C# Persian Namespace.");
}
}
}
مثال 3-6 روش ديگر ايجاد Namespace هاي تودرتو را نشان ميدهد. همانطور كه در اين مثال مشاهده مينماييد، Namespace تودرتو با قرار دادن عملگر نقطه "." در بين csharp-persian و tutorial ايجاد شده است. اين مثال از نظر كارآيي دقيقاً همانند مثال 2-6 است و از نظر پيادهسازي آسانتر از آن ميباشد.
فراخواني اعضاي يك Namespace
// Namespace اعلان
using System;
namespace csharp-persian
{
// namespace تودرتو
namespace tutorial
{
class myExample1
{
public static void myPrint1()
{
Console.WriteLine("First Example of calling another namespace member.");
}
}
}
// كلاس آغازين برنامه
class NamespaceCalling
{
// آغاز اجراي برنامه
public static void Main()
{
// چاپ خروجي در كنسول
tutorial.myExample1.myPrint1();
tutorial.myExample2.myPrint2();
}
}
}
//تودرتو بالا Namespaceمشابه Namespace
namespace csharp-persian.tutorial
{
class myExample2
{
public static void myPrint2()
{
Console.WriteLine("Second Example of calling another namespace member.");
}
}
}
مثال 4-6 چگونگي فراخواني اعضاي Namespace نشان ميدهد. در ابتداي مثال 4-6، يك Namespace تودرتو اعلان شده است، tutorial درون csharp-persian، كه داراي كلاس myExample1 و متد myPrint1 ميباشد. متد Main() اين متد را با نام tutorial.myExample1.myPrint1 فراخواني مينمايد. چون متد Main() و tutorial درون يك Namespace قرار دارند، ديگر نيازي به استفاده از نام csharp-persian غير ضروري است.
در انتهاي مثال 4-6، يك Namespace ديگر بصورت csharp-persian.tutorial آورده شده است. كلاسهاي myExamlpe1 و myExample2 هر دو متعلق به يك Namespace ميباشند، هرچند ميتواند آنها را مجزا از يكديگر نوشت در حاليكه متعلق به يك Namespace باشند. درون متد Main()، متد myPrint2 بصورت tutorial.myExample2.myPrint2 فراخواني شده است. هرچند كلاس myExample2 خارج از محدودهايست كه متد myPrint2 فراخواني شده است، نيازي به آمردن نام csharp-persian براي اين فراخواني وجود ندارد، زيرا هر دو كلاس متعلق به يك Namespace، يعني csharp-persian هستند.
توجه نماييد كه براي هر يك از كلاسهاي myExample1 و myExample2 نامهاي متفاوتي انتخاب شده است، زيرا هر عضو متعلق به يك Namespace بايد داراي نام يكتايي باشد. براي متدهاي myPrint1 و myPrint2 ، بعلت سادگي در امر يادگيري اين مطلب، نامهاي متفاوتي در نظر گرفته شده است. توجه نماييد كه اين دو متد بعلت اينكه در كلاسهاي متفاوتي قرار دارند، ميتوان نامهاي يكساني برايشان انتخاب نمود، و در اينصورت هيچگونه تداخلي بين ايندو ايجاد نخواهد شد.
استفاده از هدايتگر using
// Namespace اعلان
using System;
using csharp_station.tutorial;
// كلاس آغازين برنامه
class UsingDirective
{
// آغاز اجراي برنامه
public static void Main()
{
// Namespace فراخواني عضوي از
myExample.myPrint();
}
}
// C# PersianTutorial Namespace
namespace csharp-persian.tutorial
{
class myExample
{
public static void myPrint()
{
Console.WriteLine("Example of using a using directive.");
}
}
}
در صورتيكه مي خواهيد متدهايي را بدون استفاده از نام كامل آنها،به همراه نام Namespace، استفاده نماييد، ميتوانيد از هدايتگر using استفاده نماييد. در مثال 5-6، دوبار از هدايتگر using استفاده شده است. اولين استفاده از اين هدايتگر، using System، دقيقاً مشابه به همان چيزي است كع تا كنون در اين درسها مشاهده نمودهايد. با استفاده از اين هدايتگر ميتوانيد از اعضاي موجود در System، بدون اينكه نياز به تايپ كلمه System در هربار داشته باشيد، استفاده نماييد. در myPrint()، كلاس Console عضوي از System است كه داراي متد WriteLine() ميباشد. اگر در ابتداي برنامه از هدايتگر using استفاده نكنيم، بايد براي هر دفعه استفاده از متد WriteLine()، نام كامل آن يعني System.Console.WriteLine() را تايپ نماييم.
به طور مشابه، استفاده از using csharp-persian.tutorial امكان استفاده از اعضاي اين Namespace را، بدون نياز به تايپ نام كامل آنها فراهم مينمايد. چون در ابتداي برنامه از هدايتگر using استفاده نمودهايم، در متن برنامه متد myPrint() را بصورت myExample.myPrint() استفاده نمودهايم، درصورتيكه از هدايتگر using استفاده نميگرديم براي استفاده از اين متد بايد آنرا بصورت csharp-persian.tutorial.myExample.myPrint() ميآورديم.
استفاده از هدايتگر Alias
// Namespace اعلان
using System;
using csTut = csharp-persian.tutorial.myExample; // alias
// كلاس آغازين برنامه
class AliasDirective
{
// آغاز اجراي برنامه
public static void Main()
{
// Namespace فراخواني عضوي از
csTut.myPrint();
myPrint();
}
// متدي كه ممكن است توليد ابهام نمايد.
static void myPrint()
{
Console.WriteLine("Not a member of csharp-persian.tutorial.myExample.");
}
}
// C# Persian Tutorial Namespace
namespace csharp-persian.tutorial
{
class myExample
{
public static void myPrint()
{
Console.WriteLine("This is a member of csharp-persian.tutorial.myExample.");
}
}
}
در برخي موارد ممكن است با Namespace خاصي روبرو شويد كه داراي نامي طولاني است و تمايل داشته باشيد تا نام آنرا كوتاهتر نماييد. اين كار با استفاده از ايجاد استعاره (Alias) امكانپذير است. همانطور كه در مثال 6-6 ملاحظه مينماييد، با استفاده از
csTut = csharp-persian.tutorial.myExample يك استعاره توليد كردهايم و در متن برنامه به جاي استفاده از نام طولاني csharp-persian.tutorial.myExample از نام مستعار آن يعني csTut استفاده نمودهايم. حال از اين نام مستعار در هر جاي برنامه ميتوان استفاده كرد. ما در اين برنامه در متد Main() استفاده نمودهايم.
همچنين در متد Main()، فراخواني از متد myPrint() مربوط به كلاس AliasDirective صورت گرفته است. نام اين متد همانند myPrint() موجود در myExample است. علت اينكه ميتوان هر دو اين متدها را همزمان فراخواني كرد، استفاده از هدايتگر alias براي متد myPrint() مربوط به كلاس myExample است. (csTut.myPrint()) استفاده از اين روش باعث ميشود تا كامپايلر دريابد كه كدام متد را بايد اجرا نمايد. در صورتيكه به اشتباه از هدايتگر alias (csTut.myPrint()) استفاده نكنيم، كامپايلر به اشتباه متد myPrint() مربوط به كلاس AliasDirective را دوبار اجرا خواهد كرد.
تا اينجا،ما تنها كلاسها را در Namespace ها نشان داديم. Namespace ها انواع ديگري را نيز ميتوانند در خود داشته باشند كه در زير مشاهده مينماييد :
Classes
Structures
Interfaces
Enumerations
Delegates
خلاصه
در اين درس با Namespace آشنا شديد و فراگرفتيد كه چگونه Namespace خود را اعلان نماييد. اگر نخواهيد در هربار استفاده از متدها يا اعضاي يك Namespace، نام كامل آنها را استفاده كنيد، بايد از هدايتگر using استفاده نماييد. در صورتيكه بخواهيد بجاي استفاده از نام طولاني يك Namespace، از نامي ك.وتاهتر استفاده كنيد، بايد از هدايتگر استعارهاي (Alias Directive) استفاده نماييد.
درس هفتم – آشنايي با كلاسها در #C
در اين درس با كلاسها در زبان C# آشنا خواهيد شد. اهداف اين درس به شرح زير ميباشند :
ü پيادهسازي سازندهها (Constructors)
ü درك تفاوت بين اعضاي نمونه (Instance) و استاتيك (Static)
ü آشنايي با تخريب كنندهها (Destructors)
ü آشنايي با اعضاي كلاسها
در تمامي مطالبي كه در اين سايت مشاهده كردهايد، برنامهها داراي كلاسهايي بودهاند. در حال حاضر بايد درك نسبي از كلاسها و كار آنها و چگونگي ايجاد آنها داشته باشيد. در اين درس مروري بر آموختههاي قبلي از كلاسها خواهيم كرد و نيز با اعضاي كلاسها آشنا ميشويم.
يك كلاس با استفاده از كلمه كليدي class كه بدنبال آن نام كلاس آمده باشد، اعلان ميگردد و اعضاي اين كلاس درون {} اعلان ميگردند. هر كلاس داراي سازندهاي ميباشد كه در هربار ايجاد نمونهاي جديد از آن كلاس، بصورت خودكار فراخواني ميگردد. هدف از سازنده، تخصيصدهي اعضاي كلاس در زمان ايجاد نمونهاي جديد از كلاس است. سازندهها داراي مقادير بازگشتي نبوده و همواره نامي مشابه نام كلاس دارند. مثال 1-7 نمونهاي از يك كلاس را نشان ميدهد.
// Namespace اعلان
using System;
class OutputClass
{
string myString;
// سازنده
public OutputClass(string inputString)
{
myString = inputString;
}
// متد نمونه
public void printString()
{
Console.WriteLine("{0}", myString);
}
// تخريب كننده
~OutputClass()
{
// روتيني جهت آزادسازي برخي از منابع سيستم
}
}
// كلاس آغازين برنامه
class ExampleClass
{
// آغاز اجراي برنامه
public static void Main()
{
// OutputClass نمونهاي از
OutputClass outCl = new OutputClass("This is printed by the output class.");
// Output فراخواني متد كلاس
outCl.printString();
}
}
در مثال 1-7 دو كلاس ديده ميشوند. كلاس بالايي، كلاس OutPutClass، داراي سازنده، متد نمونه و يك تخريب كننده است. همچنين اين كلاس داراي فيلدي با نام myString است. توجه نماييد كه چگونه سازنده اين كلاس اعضاي آنرا تخصيصدهي(مقداردهي) مينمايد. در اين مثال، سازنده كلاس رشته ورودي (inputString) را بعنوان آرگومان خود دريافت مينمايد. سپس اين مقدار داخل فيلد كلاس يعني myString كپي ميگردد.
همانطور كه در ExampleClass مشاهده مينماييد، استفاده از سازنده الزامي نميباشد. در اين مورد سازنده پيش فرض ايجاد ميگردد. سازنده پيش فرض، سازندهاي بدون هيچ نوع آرگوماني است. البته شايان ذكر است كه سازندههاييي بدون آرگومان هميشه مورد استفاده نبوده و مفيد نيستند. جهت كارآمد كردن بيشتر سازندههاي بدون آرگومان بهتر است آنها را با تخصيصدهنده (Initializers) پيادهسازي نماييد. به مثال زير در اين زمينه توجه نماييد :
public OutputClass() : this("Default Constructor String") { }
فرض كنيد اين عبارت در كلاس OutPutClass در مثال 1-7 قرار داشت. اين سازنده پيش فرض به يك تخصيصدهنده همراه شده است. ":" ابتداي تخصيصدهنده را مشخص مينمايد، و به دنبال آن كلمه كليدي this آمده است. كلمه كليدي this به شيء كنوني اشاره مينمايد. استفاده از اين كلمه، فراخواني به سازنده شيء كنوني كه در آن تعريف شده است، ايجاد ميكند. بعد از كلمه كليدي this ليست پارامترها قرار ميگيرد كه در اينجا يك رشته است. عملي كه تخصيصدهنده فوق انجام ميدهد، باعث ميشود تا سازنده OutPutClass رشتهاي را بعنوان آرگومان دريافت نمايد. استفاده از تخصيصدهندهها تضمين مينمايند كه فيلدهاي كلاس شما در هنگام ايجاد نمونهاي جديد مقداردهي ميشوند.
مثال فوق نشان داد كه چگونه يك كلاس ميتواند سازندههاي متفاوتي داشته باشد. سازندهاي كه فراخواني ميشود، به تعداد و نوع آرگومانهايش وابسته است.
در زبان C#، اعضاي كلاسها دو نوع ميباشند : اعضاي نمونه و استاتيك. اعضاي نمونه كلاس متعلق به رخداد خاصي از كلاس هستند. هربار كه شياي از كلاسي خاص ايجاد ميكنيد، در حقيقت نمونه جديدي از آن كلاس ايجاد كردهايد. متد Main() در كلاس ExampleClass نمونه جديدي از OutPutClass را تحت نام outCl ايجاد مينمايد. ميتوان نمونههاي متفاوتي از كلاس OutPutClass را با نامهاي مختلف ايجاد نمود. هر يك از اين نمونههاي مجزا بوده و به تنهايي عمل ميكنند. به عنوان مثال اگر دو نمونه از كلاس OutPutClass همانند زير ايجاد نماييد :
OutputClass oc1 = new OutputClass("OutputClass1");
OutputClass oc2 = new OutputClass("OutputClass2");
با اين اعلان، شما دو نمونه از كلاس OutPutClass را ايجاد كردهايد كه يك از آنها داراي فيلد myString و متد printString() هستند و اين فيلدها و متدها كاملاً از يكديگر مجزا ميباشند. به بيان ديگر درصورتيكه عضوي از كلاس استاتيك باشد از طريق ساختار نوشتاري <class name>.<static class member> قابل دسترس خواهد بود. در اين مثال نمونهها oc1 و oc2 هستند. فرض كنيد كلاس OutPutClass داراي متد استاتيك زير باشد :
public static void staticPrinter()
{
Console.WriteLine("There is only one of me.");
}
اين متد را از درون متد Main() به صورت زير ميتوانيد فراخواني نماييد :
OutputClass.staticPrinter();
اعضاي استاتيك يك كلاس تنها از طريق نام آن كلاس قابل دسترس ميباشند و نه از طريق نام نمونه ايجاد شده از روي كلاس. بدين ترتيب براي فراخواني اعضاي استاتيك يك كلاس نيازي به ايجاد نمونه از روي آن كلاس نميباشد. همچنين تنها يك كپي از عضو استاتيك كلاس، در طول برنامه موجود ميباشد. يك مورد استفاده مناسب از اعضاي استاتيك در مواردي است كه تنها يك عمل بايد انجام گيرد و در انجام اين عمل هيچ حالت مياني وجود نداشته باشد، مانند محاسبات رياضي. در حقيقت، .Net Framework BCL خود داراي كلاس Math ميباشد كه از اعضاي استاتيك بهره ميبرد.
نوع ديگر سازندهها، سازندههاي استاتيك هستند. از سازندههاي استاتيك جهت مقداردهي فيلدهاي استاتيك يك كلاس استفاده ميشود. براي اعلان يك سازنده استاتيك تنها كافيست كه از كلمه كليدي static در جلوي نام سازنده استفاده نماييد. سازنده استاتيك قبل از ايجاد نمونه جديدي از كلاس، قبل از فراخواني عضو استاتيك و قبل از فراخواني سازنده استاتيك كلاس مشتق شده، فراخواني ميگردد. اين سازندهها تنها يكبار فراخواني ميشوند.
OutPutClass همچنين داراي يك تخريبكننده (Destructor) است. تخريبكنندهها شبيه به سازندهها هستند، با اين تفاوت كه در جلوي خود علامت "~" را دارا ميباشند. هيچ پارامتري دريافت نكرده و هيچ مقداري باز نميگردانند. از تخريبكنندهها ميتوان در هر نقطه از برنامه كه نياز به آزادسازي منابع سيستم كه در اختيار كلاس يا برنامه است، استفاده نمود. تخريبكنندهها معمولاً زماني فراخواني ميشوند كه Garbage Collector زبان C# تصميم به حذف شيء مورد استفاده برنامه از حافظه و آزادسازي منابع سيستم، گرفته باشد. (Garbage Collector يا GC، يكي از امكانات .Net Framework مخصوص زبان C# است كه سيستم بصورت اتوماتيك اقدام به آزادسازي حافظه و باز گرداندن منابع بلا استفاده به سيستم مينمايد. فراخواني GC بصورت خودكار رخ ميدهد مگر برنامهنويس بصورت صريح از طريق تخريبكنندهها آنرا فراخواني نمايد. در مباحث پيشرفتهتري كه در آينده مطرح ميكنيم خواهيد ديد كه در چه مواقعي نياز به فراخواني تخريبكنندهها بصورت شخصي داريد.)
تا كنون، تنها اعضاي كلاس كه با آنها سر و كار داشتهايد، متدها، فيلدها، سازندهها و تخريبكنندهها بودهاند در زير ليست كاملي از انواعي را كه ميتوانيد در كلاس از آنها استفاده نماييد آورده شده است :
• Constructors
• Destructors
• Fields
• Methods
• Properties
• Indexers
• Delegates
• Events
• Nested Classes
مواردي كه در اين درس با آنها آشنا نشديد، حتماً در درسهاي آينده مورد بررسي قرار خواهند گرفت.
خلاصه
در اين درس نحوه اعلان سازندههاي استاتيك و نمونه را فرا گرفتيد و با نحوه مقداردهي به فيلدها آشنا شديد. زمانيكه نياز به ايجاد نمونه از روي شيء نباشد از اعضاي استاتيك كلاس استفاده ميكنيم. با استفاده از تخريبكنندهها ميتوانيد منابع بلا استفاده را به سيستم باز گردانيد.
درس هشتم – ارثبري كلاسها
در اين درس درباره ارثبري در زبان برنامهنويسي C# صحبت خواهيم كرد. اهداف اين درس بشرح زير ميباشند :
ü پيادهسازي كلاسهاي پايه (Base Class)
ü پيادهسازي كلاسهاي مشتق شده (Derived Class)
ü مقدار دهي كلاس پايه از طريق كلاس مشتق شده
ü فراخواني اعضاي كلاس پايه
ü پنهانسازي اعضاي كلاس پايه
ارثبري يكي از مفاهيم اساسي و پايه شيگرايي است. با استفاده از اين ويژگي امكان استفاده مجدد از كد موجود فراهم ميشود. بوسيله استفاده موثر از اين ويژگي كار برنامهنويسي آسانتر ميگردد.
ارثبري(Inheritance)
using System;
public class ParentClass
{
public ParentClass()
{
Console.WriteLine("Parent Constructor.");
}
public void print()
{
Console.WriteLine("I'm a Parent Class.");
}
}
public class ChildClass : ParentClass
{
public ChildClass()
{
Console.WriteLine("Child Constructor.");
}
public static void Main()
{
ChildClass child = new ChildClass();
child.print();
}
}
خروجي اين برنامه بصورت زير است :
Parent Constructor.
Child Constructor.
I'm a Parent Class.
در مثال 1-8 دو كلاس وجود دارد. كلاس بالاي ParentClass و كلاس پائيني ChildClass است. كاري كه ميخواهيم در اينجا انجام دهيم اينست كه زير كلاسي ايجاد كنيم كه با استفاده از كدهاي موجود در ParentClass عمل نمايد.
براي اين منظور ابتدا بايد در اعلان ChildClass مشخص كنيم كه اين كلاس ميخواهد از كلاس ParentClass ارثبري داشته باشد. اين عمل با اعلان public class ChildClass : ParentClass روي ميدهد. كلاس پايه با قرار دادن ":" بعد از نام كلاس مشتق شده معين ميشود.
C# فقط از ارثبري يگانه پشتيباني مينمايد. از اينرو تنها يك كلاس پايه براي ارثبري ميتوان معين نمود. البته بايد اشاره كرد كه ارثبري چندگانه تنها از واسطها (Interfaces) امكانپذير است كه در درسهاي آينده به آنها اشاره مينماييم.
ChildClass دقيقاً توانائيهاي ParentClass را دارد. از اينرو ميتوان گفت ChildClass يك ParentClass است. (ChildClass IS a ParentClass) ChildClass داراي متد Print() مربوط به خود نيست و از متد كلاس ParentClass استفاده ميكند. نتيجه اين عمل در خط سوم خروجي ديده ميشود.
كلاسهاي پايه به طور خودكار، قبل از كلاسهاي مشتق شده نمونهاي از روي آنها ايجاد ميگردد. به خروجي مثال 1-8 توجه نماييد. سازنده ParentClass قبل از سازنده ChildClass اجرا ميگردد.
برقراري ارتباط كلاس مشتق شده با كلاس پايه
به مثال 2-8 كه در زير آمده است توجه نماييد.
using System;
public class Parent
{
string parentString;
public Parent()
{
Console.WriteLine("Parent Constructor.");
}
public Parent(string myString)
{
parentString = myString;
Console.WriteLine(parentString);
}
public void print()
{
Console.WriteLine("I'm a Parent Class.");
}
}
public class Child : Parent
{
public Child() : base("From Derived")
{
Console.WriteLine("Child Constructor.");
}
public new void print()
{
base.print();
Console.WriteLine("I'm a Child Class.");
}
public static void Main()
{
Child child = new Child();
child.print();
((Parent)child).print();
}
}
خروجي اين برنامه بشكل زير است :
From Derived
Child Constructor.
I'm a Parent Class.
I'm a Child Class.
I'm a Parent Class.
كلاسهاي مشتق شده در طول ايجاد نمونه ميتوانند با كلاس پايه خود ارتباط برقرار نمايند. در مثال 2-8 چگونگي انجام اين عمل را در سازنده ChildClass نشان ميدهد. استفاده از " : " و كلمه كليدي base باعث فراخواني سازنده كلاس پايه به همراه ليست پارامترهايش ميشود. اولين سطر خروجي، فراخواني سازنده كلاس پايه را بهمراه رشته "From Derived" نشان ميدهد.
ممكن است حالتي رخ دهد كه نياز داشته باشيد تا متد موجود در كلاس پايه را خود پيادهسازي نماييد. كلاس Child اين عمل را با اعلان متد Print() مربوط به خود انجام ميدهد. متد Print() مربوط به كلاس Child، متد Print() كلاس Parent را پنهان ميكند. نتيجه اين كار آنست كه متد Print() كلاس Parent() تا زمانيكه عمل خاصي انجام ندهيم قابل فراخواني نميباشد.
درون متد Print() كلاس Child، صريحاً متد Print() كلاس Parent را فراخواني كردهايم. اين عمل با استفاده از كلمه كليدي base قبل از نام متد انجام گرفته است. با استفاده از كلمه كليدي base ميتوان به هر يك از اعضاي public و protected كلاس پايه دسترسي داشت. خروجي مربوط به متد Print() كلاس Child در سطرها سوم و چهارم خروجي ديده ميشوند.
روش ديگر دسترسي به اعضاي كلاش پايه، استفاده از Casting صريح است. اين عمل در آخرين سطر از متد Main() كلاس Child رخ داده است. توجه داشته باشيد كه كلاس مشتق شده نوع خاصي از كلاس پايهاش ميباشد. اين مسئله باعث ميشود تا بتوان كلاس مشتق شده را مورد عمل Casting قرار داد و آنرا نمونهاي از كلاس پايهاش قرار داد. آخرين خط خروجي نشان ميدهد كه متد Print() كلاس Parent اجرا شده است.
به وجود كلمه كليدي new در متد Print() كلاس Child توجه نماييد. اين عمل باعث ميشود تا متد Print() كلاس Child متد Print() كلاس پايهاش را پنهان نمايد. درصورتيكه از كلمه كليدي new استفاده نشود، كامپايلر پيغام اخطاري را تواليد ميكند تا توجه شما را به اين مسئله جلب كند. توضيحات بيشتر در اين زمينه مربوط به مبحث چندريختي (Polymorphism) است كه در درس آينده آنرا بررسي خواهيم نمود.
خلاصه
در اين درس با روش ايجاد كلاس پايه و كلاس مشتق شده از آن آشنا شديد. متدهاي كلاي پايه را ميتوانيد بصورت صريح و يا ضمني فراخواني كنيد. همچنين متوجه شديد كه كلاس مشتق شده نوع خاصي از كلاس پايه است.
درس نهم _ چند ريختي (Polymorphism)
در اين درس به بررسي چند ريختي در زبان C# خواهيم پرداخت. اهداف اين درس عبارتند از :
چند ريختي چيست؟
پيادهسازي متد مجازي (Virtual Method)
Override كردن متد مجازي
استفاده از چند ريختي در برنامهها
يكي ديگر از مفاهيم پايهاي در شيگرايي، چند ريختي (Polymorphism) است. با استفاده از اين ويژگي، ميتوان براي متد كلاس مشتق شده پيادهسازي متفاوتي از پيادهسازي متد كلاس پايه ايجاد نمود. اين ويژگي در جايي مناسب است كه ميخواهيد گروهي از اشياء را به يك آرايه تخصيص دهيد و سپس از متد هر يك از آنها را استفاده كنيد. اين اشياء الزاما نبايد از يك نوع شيء باشند. هرچند اگر اين اشياء بواسطه ارثبري به يكديگر مرتبت باشند، ميتوان آنها را بعنوان انواع ارثبري شده به آرايه اضافه نمود. اگر هر يك از اين اشياء داراي متدي با نام مشترك باشند، آنگاه ميتوان هر يك از آنها را جداگانه پيادهسازي و استفاده نمود. در اين درس با چگونگي انجام اين عمل آشنا ميگرديد.
متد مجازي (Virtual Method)
using System;
public class DrawingObject
{
public virtual void Draw()
{
Console.WriteLine("I'm just a generic drawing object.");
}
}
مثال 1-9 كلاس DrawingObject را نشان ميدهد. اين كلاس ميتواند بعنوان كلاسي پايه چهت كلاسهاي ديگر در نظر گرفته شود. اين كلاس تنها داراي يك متد با نام Draw() ميباشد. اين متد داراي پيشوند virtual است. وجود كلمه virtual بيان ميدارد كه كلاسهاي مشتق شده از اين كلاس ميتوانند، اين متد را override نماييد و آنرا به طريقه دلخواه پيادهسازي كنند.
using System;
public class Line : DrawingObject
{
public override void Draw()
{
Console.WriteLine("I'm a Line.");
}
}
public class Circle : DrawingObject
{
public override void Draw()
{
Console.WriteLine("I'm a Circle.");
}
}
public class Square : DrawingObject
{
public override void Draw()
{
Console.WriteLine("I'm a Square.");
}
}
در مثال 2-9، سه كلاس ديده ميشود. اين كلاسها از كلاس DrawingObject ارثبري ميكنند. هر يك از اين كلاسها داراي متد Draw() هستند و تمامي آنها داراي پيشوند override ميباشند. وجود كلمه كليدي override قبل از نام متد، اين امكان را فراهم مينمايد تا كلاس، متد كلاس پايه خود را override كرده و آنرا به طرز دلخواه پيادهسازي نمايد. متدهاي override شده بايد داراي نوع و پارامترهاي مشابه متد كلاس پايه باشند.
پيادهسازي چند ريختي
using System;
public class DrawDemo
{
public static int Main( )
{
DrawingObject[] dObj = new DrawingObject[4];
dObj[0] = new Line();
dObj[1] = new Circle();
dObj[2] = new Square();
dObj[3] = new DrawingObject();
foreach (DrawingObject drawObj in dObj)
{
drawObj.Draw();
}
return 0;
}
}
مثال 3-9 برنامهاي را نشان ميدهد كه از كلاسهاي مثال 1-9 و 2-9 استفاده ميكند. در اين برنامه چند ريختي پيادهسازي شده است. در متد Main() يك آرايه ايجاد شده است. عناصر اين آرايه از نوع DrawingObject تعريف شده است. اين آرايه dObj نامگذاري شده و چهار عضو از نوع DrawingObject را در خود نگه ميدارد.
سپس آرايه dObj تخصيصدهي شده است. به دليل رابطه ارثبري اين عناصر با كلاس DrawingObject، عناصر Line، Circle و Square قابل تخصيص به اين آرايه ميباشند. بدون استفاده از اين قابليت، قابليت ارثبري، براي هر يك از اين عناصر بايد آرايهاي جدا ميساختيد. ارثبري باعث ميشود تا كلاسهاي مشتق شده بتوانند همانند كلاس پايه خود عمل كنند كه اين قابليت باعث صرفهجويي در وقت و هزينه توليد برنامه ميگردد.
پس از تخصيصدهي آرايه، حلقه foreach تك تك عناصر آنرا پيمايش مي كند. درون حلقه foreach متد Draw() براي هر يك از اعضاي آرايه اجرا ميشود. نوع شيء مرجع آرايه dObj، DrawingObject است. چون متد Draw() در هر يك از اين اشياء override ميشوند، از اينرو متد Draw() مربوط به هر يك از اين اشياء اجرا ميشوند. خروجي اين برنامه بصورت زير است :
I'm a Line.
I'm a Circle.
I'm a Square.
I'm just a generic drawing object.
متد override شده Draw() مربوط به هر يك از كلاسهاي مشتق شده در برنامه فوق همانند خروجي اجرا ميشوند. آخرين ط خروجي نيز مربوط به كلاس مجازي Draw() از كلاس DrawingObject است، زيرا آخرين عنصر آرايه شيء DrawingObject است.
خلاصه
در اين درس با مفهوم كلي چند ريختي آشنا شديد. چند ريختي امكاني است كه مخصوص زبانهاي برنامهنويسي شيگرا است و از طريق آن ميتوان براي يك متد موجود در كلاس پايه، چندين پيادهسازي متفاوت در كلاسهاي مشتق شده داشت.
درس دهم – ويژگيها در #C
در اين درس با ويژگيها (Properties) در زبان C# آشنا خواهيم شد. اهداف اين درس به شرح زير ميباشد :
موارد استفاده از Property ها
پيادهسازي Property
ايجاد Property فقط خواندني (Read-Only)
ايجاد Property فقط نوشتني (Write-Only)
Property ها امكان ايجاد حفاظت از فيلدهاي يك كلاس را از طريق خواندن و نوشتن بوسيله Property را فراهم مينمايد. Property ها علاوه بر اينكه از فيلدهاي يك كلاس حفاظت ميكنند، همانند يك فيلد قابل دسترسي هستند. بمنظور درك ارزش Property ها بهتر است ابتدا به روش كلاسيك كپسوله كردن متدها توجه نماييد.
مثال 1-10 : يك نمونه از چگونگي دسترسي به فيلدهاي كلاس به طريقه كلاسيك
using System;
public class PropertyHolder
{
private int someProperty = 0;
public int getSomeProperty()
{
return someProperty;
}
public void setSomeProperty(int propValue)
{
someProperty = propValue;
}
}
public class PropertyTester
{
public static int Main(string[] args)
{
PropertyHolder propHold = new PropertyHolder();
propHold.setSomeProperty(5);
Console.WriteLine("Property Value: {0}", propHold.getSomeProperty());
return 0;
}
}
مثال 1-10 روش كلاسيك دسترسي به فيلدهاي يك كلاس را نشان ميدهد. كلاس PropertyHolder داراي فيلدي است تمايل داريم به آن دسترسي داشته باشيم. اين كلاس داراي دو متد getSomeProperty() و setSomePropery() ميباشد. متد getSomeProperty() مقدار فيلد someProperty را باز ميگرداند و متد setSomeProperty() مقداري را به فيلد someProperty تخصيص ميدهد.
كلاس PropertyTester از متدهاي كلاس PropertyHolder جهت دريافت مقدار فيلد someProperty از كلاس PropertyHolder استفاده ميكند. در متد Main() نمونه جديدي از شي PropertyHolder با نام propHold ايجاد ميگردد. سپس بوسيله متد setSomeProperty، مقدار someMethod از propHold برابر با 5 ميگردد و سپس برنامه مقدار property را با استفاده از فراخواني متد Console.WriteLine() در خروجي نمايش ميدهد. آرگومان مورد استفاده براي بدست آوردن مقدار property فراخواني به متد getSomeProperty() است كه توسط آن عبارت “Property Value : 5” در خروجي نمايش داده ميشود.
چنين متد دسترسي به اطلاعات فيلد بسيار خوب است چرا كه از نظريه كپسوله كردن شيءگرايي پشتيباني ميكند. اگر پيادهسازي someProperty نيز تغيير يابد و مثلا از حالت int به byte تغيير يابد، باز هم اين متد كار خواهد كرد. حال همين مسئله با استفاده از خواص Property ها بسيار سادهتر پيادهسازي ميگردد. به مثال زير توجه نماييد.
مثال 2-10 : دسترسي به فيلدهاي كلاس به استفاده از Property ها
using System;
public class PropertyHolder
{
private int someProperty = 0;
public int SomeProperty
{
get
{
return someProperty;
}
set
{
someProperty = value;
}
}
}
public class PropertyTester
{
public static int Main(string[] args)
{
PropertyHolder propHold = new PropertyHolder();
propHold.SomeProperty = 5;
Console.WriteLine("Property Value: {0}", propHold.SomeProperty);
return 0;
}
}
مثال 2-10 چگونگي ايجاد و استفاده از ويژگيها (Property) را نشان ميدهد. كلاس PropertyHolder داراي پيادهسازي از ويژگي SomeProperty است. توجه نماييد كه اوليد حرف از نام ويژگي با حرف بزرگ نوشته شده و اين تنها تفاوت ميان اسم ويژگي SomeProperty و فيلد someProperty ميباشد. ويژگي داراي دو accessor با نامهاي set و get است. accessor get مقدار فيلد someProperty را باز ميگرداند. set accessor نيز با استفاده از مقدار value، مقداري را به someProperty تخصيص ميدهد. كلمه value كه در set accessor آورده شده است جزو كلمات رزرو شده زبان C# ميباشد.
كلاس PropertyTester از ويژگي someProperty مربوط به كلاس PropertyHolder استفاده ميكند. اولين خط در متد Main() شياي از نوع PropertyHolder با نام propHold ايجاد مينمايد. سپس مقدار فيلد someProperty مربوط به شيء propHold، با استفاده از ويژگي SomeProperty به 5 تغيير مييابد و ملاحظه مينماييد كه مسئله به همين سادگي است و تنها كافي است تا مقدار مورد نظر را به ويژگي تخصيص دهيم.
پس از آن، متد Console.WriteLine() مقدار فيلد someProperty شيء propHold را چاپ مينمايد. اين عمل با استفاده از ويژگي SomeProperty شيء propHold صورت ميگيرد.
ويژگيها را ميتوان طوري ايجاد نمود كه فقط خواندني (Read-Only) باشند. براي اين منظور تنها كافيست تا در ويژگي فقط از get accessor استفاده نماييم. به مثال زير توجه نماييد.
ويژگيهاي فقط خواندني (Read-Only Properties)
مثال 3-10 : ويژگيهاي فقط خواندني
using System;
public class PropertyHolder
{
private int someProperty = 0;
public PropertyHolder(int propVal)
{
someProperty = propVal;
}
public int SomeProperty
{
get
{
return someProperty;
}
}
}
public class PropertyTester
{
public static int Main(string[] args)
{
PropertyHolder propHold = new PropertyHolder(5);
Console.WriteLine("Property Value: {0}", propHold.SomeProperty);
return 0;
}
}
مثال 3-10 چگونگي ايجاد يك ويژگي فقط خواندني را نشان ميدهد. كلاس PropertyHolder داراي ويژگي SomeProperty است كه فقط get accessor را پيادهسازي ميكند. اين كلاس PropertyHolder داراي سازندهايست كه پارامتري از نوع int دريافت مينمايد.
متد Main() از كلاس PropertyTester شيء جديدي از PropertyHolder با نام propHold ايجاد مينمايد. اين نمونه از كلاس PropertyHolder از سازندة آن كه مقداري صحيح را بعنوان پارامتر دريافت ميكند، استفاده ميكند. در اين مثال اين مقدار برابر با 5 در نظر گرفته ميشود. اين امر باعث تخصيص داده شدن عدد 5 به فيلد someProperty از شيء propHold ميشود.
تا زمانيكه ويژگي SomeProperty از كلاس PropertyHolder فقط خواندني است، هيچ راهي براي تغيير مقدار فيلد someProperty وجود ندارد. بعنوان مثال در صورتيكه عبارت propHold.SomeProperty = 7 را در كد برنامه اضافه نماييد، برنامة شما كامپايل نخواهد شد چراكه ويژگي SomeProperty فقط خواندني است. اما اگر از اين ويژگي در متد Console.WriteLine() استفاده نماييد بخوبي كار خواهد كرد زيرا اين دستور تنها يك فرآيند خواندن است و با استفاده از get accessor اين عمل قابل اجرا است.
ويژگيهاي فقط نوشتني (Write-Only Properties)
به مثال زير توجه فرماييد :
مثال 4-10 : ويژگيهاي فقط خواندني
using System;
public class PropertyHolder
{
private int someProperty = 0;
public int SomeProperty
{
set
{
someProperty = value;
Console.WriteLine("someProperty is equal to {0}", someProperty);
}
}
}
public class PropertyTester
{
public static int Main(string[] args)
{
PropertyHolder propHold = new PropertyHolder();
propHold.SomeProperty = 5;
return 0;
}
}
مثال 4-10 چگونگي ايجاد و استفاده از ويژگي فقط نوشتني را نشان ميدهد. در اين حالت get accessor را از ويژگي SomeProperty حذف كرده و به جاي آن set accessor را قرار دادهايم.
متد Main() كلاس PropertyTester شياي جديد از همين كلاس با سازندة پيش فرض آن ايجاد مينمايد. سپس با استفاده از ويژگي SomeProperty از شيء propHold، مقدار 5 را به فيلد someProperty مربوط به شيء propHold تخصيص ميدهد. در اين حالت set accessor مربوط به ويژگي SomeProperty فراخواني شده و مقدار 5 را به فيلد someProperty تخصيص ميدهد و سپس عبارت someProperty is equal to 5” “را در خروجي نمايش ميدهد.
خلاصه
در اين درس با ويژگيها آشنا شديد و نحوه استفاده از آنها را فرا گرفتيد. روشهاي كلاسيك كپسوله كردن از طريق استفاده از متدهاي مجزا صورت ميگرفت ولي با استفاده از ويژگيها (Property) ميتوان به اجزاي يك شيء همانند يك فيلد دسترسي پيدا كرد. ويژگيها را ميتوان به صورت فقط خواندني و يا فقط نوشتني نيز ايجاد نمود. با استفاده از ويژگيها دسترسي مستقيم به فيلدهاي مورد نظر از يك كلاس از بين رفته و اين دسترسي تنها از طريق ويژگي مورد نظر امكانپذير ميگردد.
درس يازدهم – انديكسرها در C# (Indexers)
در اين درس با انديكسرها در C# آشنا ميشويم. اهداف اين درس به شرح زير ميباشند :
پيادهسازي انديكسر
سرريزي انديكسرها (Overload)
درك چگونگي پيادهسازي انديكسرهاي چند پارامتري
خلاصه
نكات مهم و مطالب كمكي در زمينه انديكسرها
انديكسرها
انديكسرها مفهومي بسيار ساده در زبان C# هستند. با استفاده از آنها ميتوانيد از كلاس خود همانند يك آرايه استفاده كنيد. در داخل كلاس مجموعهاي از مقادير را به هر طريقي كه مورد نظرتان هست مديريت كنيد. اين اشياؤ ميتوانند شامل مجموعهاي از اعضاي كلاس، يك آرايه ديگر، و يا مجموعهاي از ساختارهاي پيچيده دادهاي باشند، جدا از پيادهسازي داخلي كلاس، دادههاي اين ساختارها از طريق استفاده از انديكسرها قابل دسترسي هستند. به مثالي در اين زمينه توجه كنيد :
مثال 11-1 : نمونهاي از يك انديكسر
using System;
/// <summary>
/// مثالي ساده از يك انديكسر
/// </summary>
class IntIndexer
{
private string[] myData;
public IntIndexer(int size)
{
myData = new string[size];
for (int i=0; i < size; i++)
{
myData[i] = "empty";
}
}
public string this[int pos]
{
get
{
return myData[pos];
}
set
{
myData[pos] = value;
}
}
static void Main(string[] args)
{
int size = 10;
IntIndexer myInd = new IntIndexer(size);
myInd[9] = "Some Value";
myInd[3] = "Another Value";
myInd[5] = "Any Value";
Console.WriteLine("\nIndexer Output\n");
for (int i=0; i < size; i++)
{
Console.WriteLine("myInd[{0}]: {1}", i, myInd[i]);
}
}
}
مثال 11-1 نحوه پيادهسازي انديكسر را نشان ميدهد. كلاس IntIndexer داراي آراية رشتهاي بنام myData ميباشد. اين آرايه، عنصري خصوصي (private) است و كاربران خارجي (external users) نميتوانند به آن دسترسي داشته باشند. اين آرايه درون سازندة (constructor) كلاس تخصيصدهي ميگردد كه در آن پارامتر size از نوع int دريافت ميشود، از آرايه myData نمونهاي جديد ايجاد ميگردد، سپس هر يك از المانهاي آن با كلمه "empty" مقداردهي ميگردد.
عضو بعدي كلاس، انديكسر است كه بوسيلة كلمه كليدي this و دو براكت تعريف شده است، this[int pos]. اين انديكسر پارامتر موقعيتي pos را دريافت مينمايد. همانطور كه حتماً تا كنون دريافتهايد پيادهسازي انديكسر بسيار شبيه به پيادهسازي يك ويژگي (property) است. انديكسر نيز داراي accessor هاي set و get است كه دقيقاً همانند property عمل ميكنند. همانطور كه در اعلان اين انديكسر نيز مشاهده ميشود، متغيري از نوع رشتهاي را باز ميگرداند.
در متد Main() شيء جديدي از IntIndexer ايجاد شده است و مقاديري به آن افزوده ميشود و سپس نتايج چاپ ميگردند. خروجي اين برنامه به شكل زير است :
Indexer Output
myInd[0]: empty
myInd[1]: empty
myInd[2]: empty
myInd[3]: Another Value
myInd[4]: empty
myInd[5]: Any Value
myInd[6]: empty
myInd[7]: empty
myInd[8]: empty
myInd[9]: Some Value
استفاده از integer جهت دسترسي به آرايهها در اغلب زبانهاي برنامهسازي رايج است ولي زبان C# چيزي فراتر از آنرا نيز پشتيباني ميكند. در C# انديكسرها را ميتوان با چندين پارامتر تعريف كرد و هر پارامتر ميتواند از نوع خاصي باشد. پارامترهاي مختلف بوسيلة كاما از يكديگر جدا ميشوند. پارامترهاي مجاز براي انديكسر عبارتند از : integer، enum و string. علاوه بر آن، انديكسرها قابل سرريزي (Overload) هستند. در مثال 2-11 تغييراتي در مثال قبل ايجاد كردهايم تا برنامه قابليت دريافت انديكسرهاي سرريز شده را نيز داشته باشد.
سرريزي انديكسرها
مثال 2-11 : انديكسرهاي سرريز شده (Overloaded Indexers)
using System;
/// <summary>
/// پيادهسازي انديكسرهاي سرريز شده
/// </summary>
class OvrIndexer
{
private string[] myData;
private int arrSize;
public OvrIndexer(int size)
{
arrSize = size;
myData = new string[size];
for (int i=0; i < size; i++)
{
myData[i] = "empty";
}//end of for
}//end of constructor
public string this[int pos]
{
get
{
return myData[pos];
}
set
{
myData[pos] = value;
}
}//end of indexer
public string this[string data]
{
get
{
int count = 0;
for (int i=0; i < arrSize; i++)
{
if (myData[i] == data)
{
count++;
}//end of if
}//end of for
return count.ToString();
}//end of get
set
{
for (int i=0; i < arrSize; i++)
{
if (myData[i] == data)
{
myData[i] = value;
}//end of if
}//end of for
}//end of set
}//end of overloaded indexer
static void Main(string[] args)
{
int size = 10;
OvrIndexer myInd = new OvrIndexer(size);
myInd[9] = "Some Value";
myInd[3] = "Another Value";
myInd[5] = "Any Value";
myInd["empty"] = "no value";
Console.WriteLine("\nIndexer Output\n");
for (int i=0; i < size; i++)
{
Console.WriteLine("myInd[{0}]: {1}", i, myInd[i]);
}//end of for
Console.WriteLine("\nNumber of \"no value\" entries: {0}", myInd["no value"]);
}//end of Main()
}//end of class
مثال 2-11 نحوه سرريز كردن انديكسر را نشان ميدهد. اولين انديكسر كه داراي پارامتري از نوع int تحت عنوان pos است دقيقاً مشابه مثال 1-11 است ولي در اينجا انديكسر جديدي نيز وجود دارد كه پارامتري از نوع string دريافت ميكند. get accessor انديكسر جديد رشتهاي را برميگرداند كه نمايشي از تعداد آيتمهايي است كه با پارامتر مقداري data مطابقت ميكند. set accessor مقدار هر يك از مقادير ورودي آرايه را كه مقدارش با پارامتر data مطابقت نمايد را به مقداري كه به انديكسر تخصيص داده ميشود، تغيير ميدهد.
رفتار (behavior) انديكسر سرريز شده كه پارامتري از نوع string دريافت ميكند، در متد Main() نشان داده شده است. در اينجا set accessor مقدار "No value" را به تمام اعضاي كلاس myInd كه مقدارشان برابر با "empty" بوده است، تخصيص ميدهد. اين accessor از دستور زير استفاده نموده است : myInd["empty"] = "No value" . پس از اينكه تمامي اعضاي كلاس myInd چاپ شدند، تعداد اعضايي كه حاوي "No value" بودهاند نيز نمايش داده ميشوند. اين امر با استفاده از دستور زير در get accessor روي ميدهد : myInd["No value"]. خروجي برنامه بشكل زير است :
Indexer Output
myInd[0]: no value
myInd[1]: no value
myInd[2]: no value
myInd[3]: Another Value
myInd[4]: no value
myInd[5]: Any Value
myInd[6]: no value
myInd[7]: no value
myInd[8]: no value
myInd[9]: Some Value
Number of "no value" entries: 7
علت همزيستي هر دو انديكسر در مثال 2-11 در يك كلاس مشابه، تفاوت اثرگذاري و فعاليت آنهاست. اثرگذاري و تفاوت انديكسرها از تعداد و نوع پارامترهاي موجود در ليست پارامترهاي انديكسر مشخص ميگردد. در هنگام استفاده از انديكسرها نيز، كلاس با استفاده از تعداد و نوع پارامترهاي انديكسرها، ميتواند تشخيص دهد كه در يك فراخواني از كدام انديكسر بايد استفاده نمايد. نمونهاي از پيادهسازي انديكسري با چند نوع پارامتر در زير آورده شده است :
public object this[int param1, ..., int paramN]
{
get
{
// process and return some class data
}
set
{
// process and assign some class data
}
}
خلاصه :
هم اكنون شما با انديكسرها و نحوة پيادهسازي آنها آشنا شدهايد. با استفاده از انديكسرها ميتوان به عناصر يك كلاس همانند يك آرايه دسترسي پيدا كرد. در اين مبحث انديكسرهاي سرريز شده و چند پارامتري نيز مورد بررسي قرار گرفتند.
در آينده و در مباحث پيشرفتهتر با موارد بيشتري از استفادة انديكسرها آشنا خواهيد شد.
نكات :
منظور از انديكسر سرريز شده چيست؟
هنگاميكه از دو يا چند انديكسر درون يك كلاس استفاده ميكنيم، سرريزي (Overloading) انديكسرها رخ ميدهد. در هنگام فراخواني انديكسرها، كلاس تنها از روي نوع بازگشتي انديكسر و تعداد پارامترهاي آن متوجه ميشود كه منظور فراخواننده استفاده از كدام انديسكر بوده است.
از انديكسر چگونه مانند آرايه استفاده ميشود؟
همانطور كه در اين درس مشاهده كرديد دسترسي به عناصر انديكسر همانند آرايهها با استفاده از يك انديس صورت ميپذيرد. با استفاده از اين انديس ميتوان به عنصر مورد نظر كلاس دسترسي پيدا نمود.
يك مثال عملي استفاده از انديكسرها چيست؟
يك نمونة بسيار جالب از استفادة انديكسرها كنترل ListBox است. (ListBox عنصري است كنترلي كه با استفاده از آن ليستي از عناصر رشتهاي نمايش داده ميشوند و كاربر با انتخاب يكي از اين گزينهها با برنامه ارتباط برقرار ميكند. در حقيقت اين عنصر كنترلي يكي از روشهاي دريافت اطلاعات از كاربر است با اين تفاوت كه در اين روش وروديهايي كه كاربر ميتواند وارد نمايد محدود شده هستند و از قبل تعيين شدهاند. نمونهاي از يك ListBox قسمت انتخاب نوع فونت در برنامة Word است كه در آن ليستي از فونتهاي موجود در سيستم نمايش داده ميشود و كاربر با انتخاب يكي از آنها به برنامه اعلام ميكند كه قصد استفاده از كدام فونت سيستم را دارد.) ListBox نمايشي از ساختمان داده ايست شبيه به آرايه كه اعضاي آن همگي از نوع string هستند. علاوه بر اين اين كنترل ميخواهد تا در هنگام انتخاب يكي از گزينههايش بتواند اطلاعات خود را بطور خودكار update نمايد و يا به عبارتي بتواند ورودي دريافت نمايد. تمامي اين اهداف با استفاده از انديكسر ميسر ميشود. انديكسرها شبيه به property ها اعلان ميشوند با اين تفاوت مهم كه انديكسرها بدون نام هستند و نام آنها تنها كلمه كليدي this است و همين this مورد انديكس شدن قرار ميگيرد و ساير موارد بشكل پارامتر به انديكسر داده ميشوند.
public class ListBox: Control
{
private string[] items;
public string this[int index]
{
get
{
return items[index];
}
set
{
items[index] = value;
Repaint();
}
}
}
با نگاه به نحوه استفاده از انديكسر بهتر ميتوان با مفهوم آن آشنا شد. براي مثال دسترسي به ListBox بشكل زير است :
ListBox listBox = ...;
listBox[0] = "hello";
Console.WriteLine(listBox[0]);
نمونه برنامهاي كه در آن نحوة استفاده از انديكسر در عنصر كنترلي ListBox نشان داده شده، در زير آورده شده است :
Csharp-Persian_Indexer_Demo
using System;
public class ListBoxTest
{
// تخصيص داده ميشوند.ListBoxرشتههاي مورد نظر به
public ListBoxTest(params string[] initialStrings)
{
// فضايي را براي ذخيرهسازي رشتههاي تخصيص ميدهد.
strings = new String[256];
// رشتههاي وارد شده به سازنده را درون آرايهاي كپي ميكند.
foreach (string s in initialStrings)
{
strings[ctr++] = s;
}
}//end of constructor
// رشتهاي به انتهاي كنترل افزوده ميشود.
public void Add(string theString)
{
if (ctr >= strings.Length)
{
// در اين قسمت ميتوان كدي جهت كنترل پر شدن فضاي تخصيص داده شده قرار داد.
}
else
strings[ctr++] = theString;
}//end of Add()
// اعلان انديكسر
public string this[int index]
{
get
{
if (index < 0 || index >= strings.Length)
{
// در اين قسمت ميتوان كدي جهت كنترل پر شدن فضاي تخصيص داده شده قرار داد.
}
return strings[index];
}//end of get
set
{
if (index >= ctr )
{
// فراخواني متدي جهت كنترل خطا
}
else
strings[index] = value;
}//end of set
}//end of indexer
// تعداد رشتههاي موجود را نشان ميدهد
public int GetNumEntries( )
{
return ctr;
}
private string[] strings;
private int ctr = 0;
}//end of ListBoxTest class
public class Tester
{
static void Main( )
{
//جديد و تخصيص دهي آن ListBox ساخت يك
ListBoxTest lbt = new ListBoxTest("Hello", "World");
// رشتههاي مورد نظر به كنترل افزوده ميشوند.
lbt.Add("Who");
lbt.Add("Is");
lbt.Add("John");
lbt.Add("Galt");
// رشتة جديدي در خانه شمارة يك فرار داده ميشود.
string subst = "Universe";
lbt[1] = subst;
// كليه آيتمهاي موجود نمايش داده ميشوند.
for (int i = 0;i<lbt.GetNumEntries( );i++)
{
Console.WriteLine("lbt[{0}]: {1}",i,lbt[i]);
}
}//end of Main()
}//end of Tester class
خروجي نيز بشكل زير ميباشد :
Output:
lbt[0]: Hello
lbt[1]: Universe
lbt[2]: Who
lbt[3]: Is
lbt[4]: John
lbt[5]: Galt
توجه :
مطالب انتهايي اين درس كمي پيشرفتهتر و پيچيدهتر از مطالب قبل به نظر ميآيند. اين انتظار وجود ندارد كه شما كليه مطالب اين قسمت را بطور كامل متوجه شده باشيد، بلكه هدف تنها آشنا شدن شما با مسايل پيچيدهتر و واقعيتر است. در آيندهاي نه چندان دور، در سايت به صورت حرفهاي كليه مطالب و سرفصل هاي گفته شده را مورد بررسي قرار خواهيم داد. در ابتدا هدف من آشنايي شما با كليه مفاهيم پايهاي زبان C# است تا بعد از اين آشنايي به طور كامل و بسيار پيشرفته به بررسي كليه مفاهيم زبان بپردازيم. پس از اتمام آموزش اوليه تحولات اساسي در سايت مشاهده خواهيد كرد و در آن هنگام به بررسي كامل هر مبحث با مثالهايي بسيار واقعي و كاربردي خواهيم پرداخت.
درس دوازدهم – ساختارها در C# (Struct)
در اين درس با ساختارها (Struct) در زبان C# آشنا ميشويم. اهداف اين درس بشرح زير ميباشند
يك struct يا ساختار (Structure) چيست؟
پيادهسازي ساختارها(Struct)
استفاده از ساختارها(Struct)
نكات مهم و مطالب كمكي دربارة struct ها
ساختار (struct) چيست؟
همانطور كه با استفاده از كلاسها ميتوان انواع (types) جديد و مورد نظر را ايجاد نمود، با استفاده از struct ها ميتوان انواع مقداري (value types) جديد و مورد نظر را ايجاد نمود. از آنجائيكه struct ها بعنوان انواع مقداري در نظر گرفته ميشوند، از اينرو تمامي اعمال مورد استفاده بر روي انواع مقداري را ميتوان براي struct ها در نظر گرفت. struct ها بسيار شبيه به كلاسها هستند و ميتوانند داراي فيلد، متد و property باشند. عموماً ساختارها مجموعه كوچكي از عناصري هستند كه منطقي با يكديگر داراي رابطه ميباشند. براي نمونه ميتوان به ساختار Point موجود در Framework SDK اشاره كرد كه حاوي دو property با نامهاي X و Y است.
با استفاده از ساختارها (struct) ميتوان اشيايي با انواع جديد ايجاد كرد كه اين اشياء ميتوانند شبيه به انواع موجود (int, float, …) باشند. حال سوال اينست كه چه زماني از ساختارها(struct) بجاي كلاس استفاده ميكنيم؟ در ابتدا به نحوه استفاده از انواع موجود در زبان C# توجه نماييد. اين انواع داراي مقادير و عملگرهاي معيني جهت كار با اين مقادير هستند. حال اگر نياز به شياي داريد كه همانند اين انواع رفتار نمايند لازم است تا از ساختارها (struct) استفاده نماييد. در ادامه اين مبحث نكات و قوانيني را ذكر ميكنيم كه با استفاده از آنها بهتر بتوانيد از ساختارها (struct) استفاده نماييد.
اعلان و پيادهسازي struct
براي اعلان يك struct كافيست تا با استفاده از كلمه كليدي struct كه بدنبال آن نام مورد نظر براي ساختار آمده استفاده كرد. بدنة ساختار نيز بين دو كروشة باز و بسته {} قرار خواهد گرفت. به مثال زير توجه نماييد :
مثال 1-12 : نمونهاي از يك ساختار (Struct)
using System;
struct Point
{
public int x;
public int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
public Point Add(Point pt)
{
Point newPt;
newPt.x = x + pt.x;
newPt.y = y + pt.y;
return newPt;
}
}
/// <summary>
/// struct مثالي از اعلان و ساخت يك
/// </summary>
class StructExample
{
static void Main(string[] args)
{
Point pt1 = new Point(1, 1);
Point pt2 = new Point(2, 2);
Point pt3;
pt3 = pt1.Add(pt2);
Console.WriteLine("pt3: {0}:{1}", pt3.x, pt3.y);
}
}
مثال 1-12 نحوة ايجاد و استفاده از struct را نشان ميدهد. به راحتي ميتوان گفت كه يك نوع(type) ، يك struct است، زيرا از كلمه كليدي struct در اعلان خود بهره ميگيرد. ساختار پايهاي يك ساختار پايهاي يك struct بسيار شبيه به يك كلاس است، ولي تفاوتهايي با آن دارد كه اين تفاوتها در پاراگراف بعدي مورد بررسي قرار ميگيرند. ساختار Point داراي سازنده ايست كه مقادير داده شده با آنرا به فيلدهاي x و y تخصيص ميدهد. اين ساختار همچنين داراي متد Add() ميباشد كه ساختار Point ديگري را دريافت ميكند و آنرا به struct كنوني ميافزايد و سپس struct جديدي را باز ميگرداند.
توجه نماييد كه ساختار Point جديدي درون متد Add() تعريف شده است. توجه كنيد كه در اينجا همانند كلاس، نيازي به استفاده از كلمه كليدي new جهن ايجاد يك شيء جديد نميباشد. پس از آنكه نمونة جديدي از يك ساختار ايجاد شد، سازندة پيش فرض (يا همان سازندة بدون پارامترش) براي آن در نظر گرفته ميشود. سازندة بدون پارامتر كليه مقادير فيلدهاي ساختار را به مقادير پيش فرض تغيير ميدهد. بعنوان مثال فيلدهاي صحيح به صفر و فيلدهاي Boolean به false تغيير ميكنند. تعريف سازندة بدون پارامتر براي يك ساختار صحيح نميباشد. (يعني شما نميتوانيد سازندة بدون پارامتر براي يك struct تعريف كنيد.)
ساختارها (structs) با استفاده از عملگر new نيز قابل نمونهگيري هستند (هر چند نيازي به استفاده از اين عملگر نيست.) در مثال 1-12 pt1 و pt2 كه ساختارهايي از نوع Point هستند، با استفاده از سازندة موجود درون ساختار Point مقداردهي ميشوند. سومين ساختار از نوع Point، pt3 است و از سازندة بدون پارامتر استفاده ميكند زيرا در اينجا مقدار آن اهميتي ندارد. سپس متد Add() از ساختار pt1 فراخوانده ميشود و ساختار pt2 را بعنوان پارامتر دريافت ميكند. نتيجه به pt3 تخصيص داده ميشود، اين امر نشان ميدهد كه يك ساختار ميتواند همانند ساير انواع مقداري مورد استفاده قرار گيرد. خروجي مثال 1-12 در زير نشان داده شده است :
pt3 : 3 : 3
يكي ديگر از تفاوتهاي ساختار و كلاس در اينست كه ساختارها نميتوانند داراي تخريب كننده (deconstructor) باشند. همچنين ارثبري در مورد ساختارها معني ندارد. البته امكان ارثبري بين ساختارها و interface ها وجود دارد. يك interface نوع مرجعي زبان C# است كه داراي اعضايي بدون پيادهسازي است. هر كلاس و يا ساختاري كه از يك interface ارثبري نمايد بايد تمامي متدهاي آنرا پيادهسازي كند. دربارة interface ها در آينده صحبت خواهيم كرد.
خلاصه :
هم اكنون شما با چگونگي ايجاد يك ساختار آشنا شديد. هنگاميكه قصد داريد نوعي را بصورت ساختار يا كلاس پيادهسازي كنيد، بايد به اين نكته توجه كنيد كه اين نوع چگونه مورد استفاده قرار ميگيرد. اگر ميخواهيد سازندهاي بدون پارامتر داشته باشيد، در اينصورت كلاس تنها گزينه شماست. همچنين توجه نماييد از آنجائيكه يك ساختار بعنوان يك نوع مقداري در نظر گرفته ميشود، در پشته (Stack) ذخيره ميشود و حال آنكه كلاس در heap ذخيره ميگردد.
نكات مهم و مطالب كمكي
تفاوتهاي اصلي بين كلاس و ساختار در چيست؟
همانطور كه بطور مختصر در بالا نيز اشاره شد، از نظر نوشتاري (syntax) struct و كلاس بسيار شبيه به يكديگر هستند اما داراي تفاوتهاي بسيار مهمي با يكديگر ميباشند.
همانطور كه قبلاً نيز اشاره شد شما نميتوانيد براي يك struct سازندهاي تعريف كنيد كه بدون پارامتر است، يعني براي ايجاد سازنده براي يك struct حتماً بايد اين سازنده داراي پارامتر باشد. به قطعه كد زير توجه كنيد :
struct Time
{
public Time() { ... } // خطاي زمان كامپايل رخ ميدهد
⋮
}
پس از اجراي كد فوق كامپايلر خطايي را ايجاد خواهد كرد بدين عنوان كه سازندة struct حتماٌ بايد داراي پارامتر باشد. حال اگر بجاي struct از كلمه كليدي calss استفاده كرده بوديم اين كد خطايي را ايجاد نميكرد. در حقيقت تفاوت در اينست كه در مورد struct، كامپايلر اجازة ايجاد سازندة پيش فرض جديدي را به شما نميدهد ولي در مورد كلاس چنين نيست. هنگام اعلان كلاس در صورتيكه شما سازندة پيش فرضي اعلان نكرده باشيد، كامپايلر سازندهاي پيش فرض براي آن در نظر ميگيرد ولي در مورد struct تنها سازندة پيش فرضي معتبر است كه كامپايلر آنرا ايجاد نمايد نه شما !
يكي ديگر از تفاوتهاي بين كلاس و struct در آن است كه، اگر در كلاس برخي از فيلدهاي موجود در سازندة كلاس را مقداردهي نكنيد، كامپايلر مقدار پيش فرض صفر، false و يا null را براي آن فيلد در نظر خواهد گرفت ولي در struct تمامي فيلدهاي سازنده بايد بطور صريح مقداردهي شوند و درصورتيكه شما فيلدي را مقداردهي نكيد كامپايلر هيچ مقداري را براي آن در نظر نخواهدگرفت و خطاي زمان كامپايل رخ خواهد داد. بعنوان مثال در كد زير اگر Time بصورت كلاس تعريف شده بود خطايي رخ نميداد ولي چون بصورت struct تعريف شده خطاي زمان كامپايل رخ خواهد داد :
struct Time
{
public Time(int hh, int mm)
{
hours = hh;
minutes = mm;
} // خطاي زمان كامپايلي بدين صورت رخ ميدهد : seconds not initialized
⋮
private int hours, minutes, seconds;
}
تفاوت ديگر كلاس و struct در اينست كه در كلاس ميتوانيد در هنگام اعلان فيلدها را مقداردهي كنيد حال آنكه در struct چنين عملي باعث ايجاد خطاي زمان كامپايل خواهد شد. همانند كدهاي فوق، در كد زير اگر از كلاس بجاي struct استفاده شده بود خطا رخ نميداد :
struct Time
{
⋮
private int hours = 0; // خطاي زمان كامپايل رخ ميدهد
private int minutes;
private int seconds;
}
آخرين تفاوت بين كلاس و struct كه ما به آن خواهيم پرداخت در مورد ارثبري است. كلاسها ميتوانند از كلاس پاية خود ارثبري داشته باشند در حاليكه ارثبري در struct ها معنايي ندارد و يك struct تنها ميتواند از واسطها (interface) ارثبري داشته باشد.
پس از ايجاد يك ساختار چگونه ميتوان از آن استفاده نمود؟
همانطور كه گفتيم، ساختارها روشي براي ايجاد انواع جديد مقدار (Value Types) هستند. از اينرو پس از ايجاد يك ساختار ميتوان از آن همانند ساير انواع مقداري استفاده نمود. براي استفاده از يك ساختار ايجاد شده كافيست تا نام آنرا قبل از متغير مورد نظر قرار دهيم تا متغير مورد نظر از نوع آن ساختار خاص تعريف شود.
struct Time
{
⋮
private int hours, minutes, seconds;
}
class Example
{
public void Method(Time parameter)
{
Time localVariable;
⋮
}
private Time field;
}
آخرين نكتهاي كه در مورد ساختارها براي چندمين بار اشاره ميكنم انست كه، ساختارها انواع مقداري هستند و مستقيماً مقدار را در خود نگه ميدارند و از اينرو در stack نگهداري ميشوند. استفاده از ساختارها همانند ساير انواع مقداري است.
دل آرام...
12-15-2012, 03:37 PM
:72::72::72:
Powered by vBulletin™ Version 4.2.0 Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.