درس هفتم – آشنایی با کلاسها در C#
در این درس با کلاسها در زبان C# آشنا خواهید شد. اهداف این درس به شرح زیر میباشند :
ü پیادهسازی سازندهها (Constructors)
ü درک تفاوت بین اعضای نمونه (Instance) و استاتیک (Static)
ü آشنایی با تخریب کنندهها (Destructors)
ü آشنایی با اعضای کلاسها
در تمامی مطالبی که در این سایت مشاهده کردهاید، برنامهها دارای کلاسهایی بودهاند. در حال حاضر باید درک نسبی از کلاسها و کار آنها و چگونگی ایجاد آنها داشته باشید. در این درس مروری بر آموختههای قبلی از کلاسها خواهیم کرد و نیز با اعضای کلاسها آشنا میشویم.
یک کلاس با استفاده از کلمه کلیدی class که بدنبال آن نام کلاس آمده باشد، اعلان میگردد و اعضای این کلاس درون {} اعلان میگردند. هر کلاس دارای سازندهای میباشد که در هربار ایجاد نمونهای جدید از آن کلاس، بصورت خودکار فراخوانی میگردد. هدف از سازنده، تخصیصدهی اعضای کلاس در زمان ایجاد نمونهای جدید از کلاس است. سازندهها دارای مقادیر بازگشتی نبوده و همواره نامی مشابه نام کلاس دارند. مثال 1-7 نمونهای از یک کلاس را نشان میدهد.
// Namespace اعلان
using System;
class OutputClass
{
string myString;
// سازنده
public OutputClass(string inputString)
{
myString = inputString;
}
// متد نمونه
public void printString()
{
Console.WriteLine("{0}", myString);
}
// تخریب کننده
~OutputClass()
{
// روتینی جهت آزادسازی برخی از منابع سیستم
}
}
// کلاس آغازین برنامه
class ExampleClass
{
// آغاز اجرای برنامه
public static void Main()
{
// OutputClass نمونهای از
OutputClass outCl = new OutputClass("This is printed by the output class.");
// Output فراخوانی متد کلاس
outCl.printString();
}
}
در مثال 1-7 دو کلاس دیده میشوند. کلاس بالایی، کلاس OutPutClass، دارای سازنده، متد نمونه و یک تخریب کننده است. همچنین این کلاس دارای فیلدی با نام myString است. توجه نمایید که چگونه سازنده این کلاس اعضای آنرا تخصیصدهی(مقداردهی) مینماید. در این مثال، سازنده کلاس رشته ورودی (inputString) را بعنوان آرگومان خود دریافت مینماید. سپس این مقدار داخل فیلد کلاس یعنی myString کپی میگردد.
همانطور که در ExampleClass مشاهده مینمایید، استفاده از سازنده الزامی نمیباشد. در این مورد سازنده پیش فرض ایجاد میگردد. سازنده پیش فرض، سازندهای بدون هیچ نوع آرگومانی است. البته شایان ذکر است که سازندههاییی بدون آرگومان همیشه مورد استفاده نبوده و مفید نیستند. جهت کارآمد کردن بیشتر سازندههای بدون آرگومان بهتر است آنها را با تخصیصدهنده (Initializers) پیادهسازی نمایید. به مثال زیر در این زمینه توجه نمایید :
public OutputClass() : this("Default Constructor String") { }
فرض کنید این عبارت در کلاس OutPutClass در مثال 1-7 قرار داشت. این سازنده پیش فرض به یک تخصیصدهنده همراه شده است. ":" ابتدای تخصیصدهنده را مشخص مینماید، و به دنبال آن کلمه کلیدی this آمده است. کلمه کلیدی this به شیء کنونی اشاره مینماید. استفاده از این کلمه، فراخوانی به سازنده شیء کنونی که در آن تعریف شده است، ایجاد میکند. بعد از کلمه کلیدی this لیست پارامترها قرار میگیرد که در اینجا یک رشته است. عملی که تخصیصدهنده فوق انجام میدهد، باعث میشود تا سازنده OutPutClass رشتهای را بعنوان آرگومان دریافت نماید. استفاده از تخصیصدهندهها تضمین مینمایند که فیلدهای کلاس شما در هنگام ایجاد نمونهای جدید مقداردهی میشوند.
مثال فوق نشان داد که چگونه یک کلاس میتواند سازندههای متفاوتی داشته باشد. سازندهای که فراخوانی میشود، به تعداد و نوع آرگومانهایش وابسته است.
در زبان C#، اعضای کلاسها دو نوع میباشند : اعضای نمونه و استاتیک. اعضای نمونه کلاس متعلق به رخداد خاصی از کلاس هستند. هربار که شیای از کلاسی خاص ایجاد میکنید، در حقیقت نمونه جدیدی از آن کلاس ایجاد کردهاید. متد Main() در کلاس ExampleClass نمونه جدیدی از OutPutClass را تحت نام outCl ایجاد مینماید. میتوان نمونههای متفاوتی از کلاس OutPutClass را با نامهای مختلف ایجاد نمود. هر یک از این نمونههای مجزا بوده و به تنهایی عمل میکنند. به عنوان مثال اگر دو نمونه از کلاس OutPutClass همانند زیر ایجاد نمایید :
OutputClass oc1 = new OutputClass("OutputClass1");
OutputClass oc2 = new OutputClass("OutputClass2");
با این اعلان، شما دو نمونه از کلاس OutPutClass را ایجاد کردهاید که یک از آنها دارای فیلد myString و متد printString() هستند و این فیلدها و متدها کاملاً از یکدیگر مجزا میباشند. به بیان دیگر درصورتیکه عضوی از کلاس استاتیک باشد از طریق ساختار نوشتاری
public static void staticPrinter()
{
Console.WriteLine("There is only one of me.");
}
این متد را از درون متد Main() به صورت زیر میتوانید فراخوانی نمایید :
OutputClass.staticPrinter();
اعضای استاتیک یک کلاس تنها از طریق نام آن کلاس قابل دسترس میباشند و نه از طریق نام نمونه ایجاد شده از روی کلاس. بدین ترتیب برای فراخوانی اعضای استاتیک یک کلاس نیازی به ایجاد نمونه از روی آن کلاس نمیباشد. همچنین تنها یک کپی از عضو استاتیک کلاس، در طول برنامه موجود میباشد. یک مورد استفاده مناسب از اعضای استاتیک در مواردی است که تنها یک عمل باید انجام گیرد و در انجام این عمل هیچ حالت میانی وجود نداشته باشد، مانند محاسبات ریاضی. در حقیقت، .Net Framework BCL خود دارای کلاس Math میباشد که از اعضای استاتیک بهره میبرد.
نوع دیگر سازندهها، سازندههای استاتیک هستند. از سازندههای استاتیک جهت مقداردهی فیلدهای استاتیک یک کلاس استفاده میشود. برای اعلان یک سازنده استاتیک تنها کافیست که از کلمه کلیدی static در جلوی نام سازنده استفاده نمایید. سازنده استاتیک قبل از ایجاد نمونه جدیدی از کلاس، قبل از فراخوانی عضو استاتیک و قبل از فراخوانی سازنده استاتیک کلاس مشتق شده، فراخوانی میگردد. این سازندهها تنها یکبار فراخوانی میشوند.
OutPutClass همچنین دارای یک تخریبکننده (Destructor) است. تخریبکنندهها شبیه به سازندهها هستند، با این تفاوت که در جلوی خود علامت "~" را دارا میباشند. هیچ پارامتری دریافت نکرده و هیچ مقداری باز نمیگردانند. از تخریبکنندهها میتوان در هر نقطه از برنامه که نیاز به آزادسازی منابع سیستم که در اختیار کلاس یا برنامه است، استفاده نمود. تخریبکنندهها معمولاً زمانی فراخوانی میشوند که Garbage Collector زبان C# تصمیم به حذف شیء مورد استفاده برنامه از حافظه و آزادسازی منابع سیستم، گرفته باشد. (Garbage Collector یا GC، یکی از امکانات .Net Framework مخصوص زبان C# است که سیستم بصورت اتوماتیک اقدام به آزادسازی حافظه و باز گرداندن منابع بلا استفاده به سیستم مینماید. فراخوانی GC بصورت خودکار رخ میدهد مگر برنامهنویس بصورت صریح از طریق تخریبکنندهها آنرا فراخوانی نماید. در مباحث پیشرفتهتری که در آینده مطرح میکنیم خواهید دید که در چه مواقعی نیاز به فراخوانی تخریبکنندهها بصورت شخصی دارید.)
تا کنون، تنها اعضای کلاس که با آنها سر و کار داشتهاید، متدها، فیلدها، سازندهها و تخریبکنندهها بودهاند در زیر لیست کاملی از انواعی را که میتوانید در کلاس از آنها استفاده نمایید آورده شده است :
• Constructors
• Destructors
• Fields
• Methods
• Properties
• Indexers
• Delegates
• Events
• Nested Classes
مواردی که در این درس با آنها آشنا نشدید، حتماً در درسهای آینده مورد بررسی قرار خواهند گرفت.
خلاصه
در این درس نحوه اعلان سازندههای استاتیک و نمونه را فرا گرفتید و با نحوه مقداردهی به فیلدها آشنا شدید. زمانیکه نیاز به ایجاد نمونه از روی شیء نباشد از اعضای استاتیک کلاس استفاده میکنیم. با استفاده از تخریبکنندهها میتوانید منابع بلا استفاده را به سیستم باز گردانید.
درس ششم – 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# آشنا میشوید. اهداف این درس به شرح زیر میباشد :
ü درک ساختار یک متد
ü درک تفاوت بین متدهای استاتیک (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 ");
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 ");
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 ");
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 ");
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 = "Hadi";
addr.address = "C# Persian";
this.addAddress(ref addr);
break;
case "D":
case "d":
addr.name = "Salehy";
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("Hadi", "Salehy", "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 = "Hadi";
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 است مگر آنکه صریحاً مشخص گردد.