درس یازدهم – اندیکسرها در C# (Indexers)

درس یازدهم – اندیکسرها در C# (Indexers)

 

در این درس با اندیکسرها در C# آشنا می‌شویم. اهداف این درس به شرح زیر می‌باشند :

  • پیاده‌سازی اندیکسر
  • سرریزی اندیکسرها (Overload)
  • درک چگونگی پیاده‌سازی اندیکسرهای چند پارامتری
  • خلاصه
  • نکات مهم و مطالب کمکی در زمینه اندیکسرها

 

اندیکسرها

اندیکسرها مفهومی بسیار ساده در زبان C# هستند. با استفاده از آنها می‌توانید از کلاس خود همانند یک آرایه استفاده کنید. در داخل کلاس مجموعه‌ای از مقادیر را به هر طریقی که مورد نظرتان هست مدیریت کنید. این اشیاؤ می‌توانند شامل مجموعه‌ای از اعضای کلاس، یک آرایه دیگر، و یا مجموعه‌ای از ساختارهای پیچیده داده‌ای باشند، جدا از پیاده‌سازی داخلی کلاس، داده‌های این ساختارها از طریق استفاده از اندیکسرها قابل دسترسی هستند. به مثالی در این زمینه توجه کنید :

 

مثال 11-1 : نمونه‌ای از یک اندیکسر

using System;

///

/// مثالی ساده از یک اندیکسر

///

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(" Indexer Output ");

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;

///

/// پیاده‌سازی اندیکسرهای سرریز شده

///

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(" Indexer Output ");

for (int i=0; i < size; i++)

{

Console.WriteLine("myInd[{0}]: {1}", i, myInd[i]);

}//end of for

Console.WriteLine(" Number 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

}

}

خلاصه :

هم اکنون شما با اندیکسرها و نحوة پیاده‌سازی آنها آشنا شده‌اید. با استفاده از اندیکسرها می‌توان به عناصر یک کلاس همانند یک آرایه دسترسی پیدا کرد. در این مبحث اندیکسرهای سرریز شده و چند پارامتری نیز مورد بررسی قرار گرفتند.

در آینده و در مباحث پیشرفته‌تر با موارد بیشتری از استفادة اندیکسرها آشنا خواهید شد.

 نکات :

  1. منظور از اندیکسر سرریز شده چیست؟

 هنگامیکه از دو یا چند اندیکسر درون یک کلاس استفاده می‌کنیم، سرریزی (Overloading) اندیکسرها رخ می‌دهد. در هنگام فراخوانی اندیکسرها، کلاس تنها از روی نوع بازگشتی اندیکسر و تعداد پارامترهای آن متوجه می‌شود که منظور فراخواننده استفاده از کدام اندیسکر بوده است.

  1. از اندیکسر چگونه مانند آرایه استفاده می‌شود؟

همانطور که در این درس مشاهده کردید دسترسی به عناصر اندیکسر همانند آرایه‌ها با استفاده از یک اندیس صورت می‌پذیرد. با استفاده از این اندیس می‌توان به عنصر مورد نظر کلاس دسترسی پیدا نمود.

  1. یک مثال عملی  استفاده از  اندیکسرها چیست؟

یک نمونة بسیار جالب از استفادة اندیکسرها کنترل 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

{

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# است تا بعد از این آشنایی به طور کامل و بسیار پیشرفته به بررسی کلیه مفاهیم زبان بپردازیم. پس از اتمام آموزش اولیه تحولات اساسی در سایت مشاهده خواهید کرد و در آن هنگام به بررسی کامل هر مبحث با مثال‌هایی بسیار واقعی و کاربردی خواهیم پرداخت.

نظرات 2 + ارسال نظر
نوشین دوشنبه 2 مرداد‌ماه سال 1385 ساعت 13:15

سلام
خسته نباشد
من تازه دارم #C یاد میگیرم
وبلاگ خوبی دارین

از اینکه به ما لطف دارین متشکر هستیم
امید وارم دوباره به ما سر بزنین ...
موفق باشین
.............................صالی جون

مهرداد امینی سه‌شنبه 3 مرداد‌ماه سال 1385 ساعت 01:53 http://www.pcmehrdad.com

سلام خسته نباشی
اگه می خواهی با هم تبادل لینک داشته باشیم من ورودی ۸۴ بوعلی کامپیوترم

از اینکه به ما لطف دارین متشکر هستیم
امید وارم دوباره به ما سر بزنین ...
موفق باشین

برای نمایش آواتار خود در این وبلاگ در سایت Gravatar.com ثبت نام کنید. (راهنما)
ایمیل شما بعد از ثبت نمایش داده نخواهد شد