درس یازدهم – اندیکسرها در C# (Indexers)
در این درس با اندیکسرها در C# آشنا میشویم. اهداف این درس به شرح زیر میباشند :
اندیکسرها
اندیکسرها مفهومی بسیار ساده در زبان 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
}
}
خلاصه :
هم اکنون شما با اندیکسرها و نحوة پیادهسازی آنها آشنا شدهاید. با استفاده از اندیکسرها میتوان به عناصر یک کلاس همانند یک آرایه دسترسی پیدا کرد. در این مبحث اندیکسرهای سرریز شده و چند پارامتری نیز مورد بررسی قرار گرفتند.
در آینده و در مباحث پیشرفتهتر با موارد بیشتری از استفادة اندیکسرها آشنا خواهید شد.
نکات :
هنگامیکه از دو یا چند اندیکسر درون یک کلاس استفاده میکنیم، سرریزی (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
{
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 یاد میگیرم
وبلاگ خوبی دارین
از اینکه به ما لطف دارین متشکر هستیم
امید وارم دوباره به ما سر بزنین ...
موفق باشین
.............................صالی جون
سلام خسته نباشی
اگه می خواهی با هم تبادل لینک داشته باشیم من ورودی ۸۴ بوعلی کامپیوترم
از اینکه به ما لطف دارین متشکر هستیم
امید وارم دوباره به ما سر بزنین ...
موفق باشین