متدها
در اين قسمت با متدها در زبان 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 است مگر آنكه صريحاً مشخص گردد.