درس ششم – Namespaces

 درس ششم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 است مگر آنکه صریحاً مشخص گردد.

درس چهارم – دستورالعمل‌های کنترلی، حلقه‌ها

درس چهارم – دستورالعمل‌های کنترلی، حلقه‌ها

 

 در این درس نحوه استفاده از دستورالعمل‌های کنترل حلقه در زبان 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 ");

      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();

      // تصمیمی بر اساس ورودی کاربر گرفته می‌شود

      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

{ } while ();

 دستورالعملهای مورد استفاده در بلوک این دستور، هر دستورالعمل معتبر زبان 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 لیستی از عبارات است که بوسیله کاما از یکدیگر جدا می‌شوند. این عبارات تنها یکبار در طول دوره کاری حلقه 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 = {"Hadi", "Salehy", "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 تغییر می‌نماید.