آرایه‌ها در C#

09 فروردین 1398
درسنامه درس 5 از سری آموزش سی شارپ (C#)
csharp-arrays

با مطالعه‌ی فصول گذشته مبانی و مقدمات زبان برنامه‌نویسی C# را فرا گرفتید. انواع حلقه‌ها، دستورهای شرطی و کنترلی، عملگرهای محاسباتی و منطقی و ... را مورد بررسی قرار دادیم و در نهایت انواع داده را شناختیم. حال در این فصل به توضیح کامل آرایه‌ها می‌پردازیم. آرایه‌ها به عنوان یک مجموعه از داده‌ها با نوع یکسان می‌باشد. با ما همراه باشید.

مقدمه

گاهی میخواهیم مجموعه‌ای از مقادیر متغییرها را درون یک بسته بندی قرار داده و در طول برنامه‌ی خود در اختیار داشته باشیم. برای اینکار باید از یک ساختار داده به نام آرایه استفاده کنیم. از آنجا که هر آنچه در زبان برنامه‌‎نویسی سی شارپ وجود دارد به عنوان یک شیء شاخته می‌شود، آرایه‌ها نیز یک شیءمی‌باشند که از کلاس نوع خود مشتق شده‌اند. در ارتباط با اشیاء در زبان برنامه‌نویسی سی شارپ بعدا به تفصیل توضیح خواهیم داد.

تعریف یک آرایه

تعریف: آرایه عبارت است از یک ساختار داده‌ای که شامل چندین متغییر با نوع یکسان و اندازه‌ی مشخص است. تصویر زیر یک شماتیک از آرایه‌ها را برای شما به نمایش می‌دهد:

شماتیک آرایه‌ها در زبان برنامه‌نویسی #C

آرایه‌ها به صورت زیر تعریف می‌شوند:

datatype[] arrayName;

با مشاهده دستور فوق متوجه خواهید شد که ابتدا نوع متغییر را تعریف کرده و سپس یک جفت براکت جهت معرفی آرایه قرار داده و در نهایت نام آرایه‌ی موردنظر را نوشته و مقدار آرایه را به آن انتساب می‌دهیم.

به عنوان مثال برای تعریف یک آرایه به نام roxo‌ از نوع متغییر double خواهیم داشت:

double[] roxo;

مقدار‌دهی اولیه یک آرایه

با تعریف یک آرایه، مقداردهی‌ای روی آن صورت نمی‌پذیرد. هنگامیکه مقداردهی اولیه انجام شود شما می‌توانید یک مقدار مشخص را به هر خانه از آرایه اختصاص دهید. با توجه به اینکه آرایه‌ها به عنوان یک شیء از کلاس آرایه مطرح می‌شوند، مقداردهی اولیه آنها به صورت زیر می‌باشد:

double[] roxo = new double[10]

با این دستور یک آرایه با ۱۰ خانه تولید شده است که مقدار هر خانه در حال حاضر برابر «هیچ یا خالی» است.

تخصیص مقادیر به آرایه‌ها

برای تخصیص مقادیر به المان‌های هر آرایه، از شماره‌های ایندکس (شمارنده هر المان) استفاده می‌شود. به این معنی که مثلا خانه‌ی اول هر آرایه از ۰ شمارش شده و تا ۹ ادامه می‌یابد که در مجموع ۰ تا ۹ برابر ۱۰ خانه می‌شود. بنابراین برای تخصیص از دستور زیر استفاده می‌کنیم.

double[] roxo = new double[10];
roxo[0] = 4500.0;

همچنین می‌توانید مقادیر را به صورت دسته‌جمعی به یک آرایه اختصاص دهید که در این حالت داریم:

double[] roxo = { 2340.0, 4523.69, 3421.0}

یا می‌توان به هنگام مقداردهی اولیه این مقادیر را اختصاص دهیم:

int [] marks = new int[5]  { 99,  98, 92, 97, 95};

از طرفی با حذف تعداد خانه‌های یک آرایه ( عدد ۵ در مثال بالا) می‌توان یک آرایه را به صورت زیر تعریف کرد:

int [] marks = new int[]  { 99,  98, 92, 97, 95};

در این حالت یک آرایه به سایز ۵ (۰-۱-۲-۳-۴ خانه) در اختیار قرار می‌گیرد. همچنین می‌توان یک متغییر آرایه را درون یک متغییر آرایه دیگر کپی کرد که در این حالت متغییری که اطلاعات را درون آن کپی کردیم و متغییر اصلی دارای یک مکان حافظه هستند. به مثال زیر توجه کنید:

int [] marks = new int[]  { 99,  98, 92, 97, 95};
int[] score = marks;

دسترسی به المان‌های یک آرایه

یک المان توسط ایندکس‌های آرایه در دسترس خواهد بود. برای اینکار کافیست ایندکس آرایه را درون براکت آن متغییر قرار دهیم. به نمونه‌ی زیر توجه کنید.

double salary = roxo[9];

با دستور فوق اطلاعات مربوط به خانه‌ی شماره ۱۰ (ایندکس ۹) در اختیار قرار بگیرد و درون متغییر salary‌ ذخیره گردد.

به مثال زیر توجه کنید، در این مثال می‌خواهیم اطلاعات تک تک درایه‌های یک آرایه با ۱۰ خانه را نمایش دهیم. توجه داشته باشید که به هنگام تعریف آرایه‌هایی از نوع int مقداردهی اولیه بگونه‌ای است که در ابتدا تمام درایه‌ها ۰ می‌باشد. در این مثال تک تک درایه‌ها با عدد ۱۰۰ جمع شده و مقدار حاصله به همراه نمایش ایندکس آنها چاپ می‌شود:

using System;
namespace ArrayApplication
{
   class MyArray
   {
      static void Main(string[] args)
      {
         int []  n = new int[10]; /* n is an array of 10 integers */
         int i,j;

         /* initialize elements of array n */
         for ( i = 0; i < 10; i++ )
         {
            n[ i ] = i + 100;
         }
         
         /* output each array element's value */
         for (j = 0; j < 10; j++ )
         {
            Console.WriteLine("Element[{0}] = {1}", j, n[j]);
         }
         Console.ReadKey();
      }
   }
}

بنابراین خروجی مثال فوق به صورت زیر خواهد بود:

Element[0] = 100
Element[1] = 101
Element[2] = 102
Element[3] = 103
Element[4] = 104
Element[5] = 105
Element[6] = 106
Element[7] = 107
Element[8] = 108
Element[9] = 109

استفاده از حلقه‌ی foreach

یکی از حلقه‌هایی که در این بخش توضیح خواهیم داد، حلقه‌ی foreach‌ به معنای «به ازای هر» می‌باشد. از این حلقه برای دسترسی به تک تک المان‌ها و آیتم‌های یک آرایه استفاده می‌شود تا از تکرار یک دستور خودداری کنیم. به مثال زیر توجه کنید:

using System;
namespace ArrayApplication
{
   class MyArray
   {
      static void Main(string[] args)
      {
         int []  narray = new int[5] {1,2,3,4,5}; /* n is an array of 10 integers */
         
         foreach ( int i in narray)
         {
             Console.WriteLine("Element = {0}", i);
         }
         
         Console.ReadKey();
      }
   }
}

در این حالت تک تک درایه‌های یک آرایه چاپ می‌شود و خروجی به صورت زیر خواهد بود:

Element = 1
Element = 2
Element = 3
Element = 4
Element = 5

آرایه‌های چند بعدی

تا به اینجای کار آرایه‌های یک بعدی را مورد بررسی قرار دادیم اما در زبان برنامه‌نویسی سی شارپ گونه‌ای دیگر از آرایه‌ها وجود دارند که ابعاد آنها متفاوت تر است. معمول‌ترین این نوع، آرایه‌های دو بعدی می‌باشند که به صورت زیر تعریف می‌شوند:

string [,] names;

یا اگر بخواهیم یک آرایه‌ی ۳ بعدی تعریف کنیم آنگاه داریم:

int [ , , ] m;

آرایه‌های دوبعدی

این نوع آرایه‌ها همانطور که قبلا ذکر کردیم به عنوان معمول‌ترین نوع آرایه‌ در زبان برنامه‌نویسی سی‌شارپ مورد استفاده قرار می‌گیرند. ماتریس‌ها در ریاضیات به عنوان یکی از معروف‌ترین نوع آرایه‌های دو بعدی شناخته می‌شوند. با بررسی تصویر زیر چگونگی تقسیم آرایه‌های دو بعدی را به ۳ سطر و ۴ ستون مشاهده می‌کنید:

آرایه های دو بعدی در زبان برنامه‌نویسی #C

دسترسی به مقادیر آرایه‌های دو‌بعدی

همانطور که ملاحظه می‌کنید مثلا برای دسترسی به درایه‌ی سطر ۲ و ستون ۳ باید دستور زیر را بنویسیم:

int valueofRow23 = roxo[2,3]

مقدار دهی اولیه‌ی آرایه‌های دو بعدی

برای مقدار دهی اولیه این نوع آرایه‌ها می‌توان مشابه روش مقداردهی برای آرایه‌ی یک بعدی استفاده کرد ولی با این تفاوت که مقادیر این نوع آرایه‌ها به صورت تو در تو می‌باشد. به عنوان مثال برای مقداردهی یک آرایه با ۳ سطر و ۴ ستون به صورت زیر عمل می‌کنیم:

int [,] a = new int [3,4] {
   {0, 1, 2, 3} ,   /*  initializers for row indexed by 0 */
   {4, 5, 6, 7} ,   /*  initializers for row indexed by 1 */
   {8, 9, 10, 11}   /*  initializers for row indexed by 2 */
};

آرایه‌های دندانه‌دار (Jagged Array)

این نوع آرایه‌ها معمولا دارای سطرهایی با طول متغییر می‌باشند. یعنی به‌گونه‌ای یک آرایه‌ی ناهموار هستند. به عبارتی آرایه‌ی دندانه‌دار را می‌توان به عنوان آرایه‌ای از آرایه‌ها در نظر گرفت. ساختار کلی آنها به صورت زیر می‌باشد:

datatype[] arrayName;

که در این ساختار datatype‌ نوع آرایه و سپس دو جفت براکت و در نهایت نام آرایه arrayName نام‌گذاری می‌شود. ممکن است مقداردهی به این آرایه‌ها کمی گیج کننده به نظر برسد اما با کمی دقت همه چیز حل می‌شود. به مثال زیر توجه کنید:

int[][] myArrays = new int[3][];

myArrays[0] = new int[3];
myArrays[1] = new int[5];
myArrays[2] = new int[2];

اگر بخواهیم ساختار این آرایه را به صورت یک تصویر رسم کنیم، شکل زیر ایجاد خواهد شد:

آرایه‌های دندانه‌دار (Jagged Array)

حال اگر بخواهیم هر ردیف را مقداردهی کنیم باید دستور زیر را بنویسیم:

int[][] myArrays = new int[3][];

myArrays[0] = new int[3] { 1, 2, 3 };
myArrays[1] = new int[5] { 5, 4, 3, 2, 1 };
myArrays[2] = new int[2] { 11, 22 };

برای دسترسی ساده‌تر به مقداردهی این آرایه‌ها از روش زیر استفاده خواهیم کرد:

int[][] myArrays = new int[3][] { new int[3] { 1, 2, 3 },
                                  new int[5] { 5, 4, 3, 2, 1 },
                                  new int[2] { 11, 22 } };

یعنی ابتدا سطر آرایه را مشخص و سپس درون مقداردهی، مقدار هر ستون را مشخص کرده‌ایم. همچنین می‌توان برای تعریف طول سطرها را نیز مشخص نکرد. بنابراین داریم:

int[][] myArrays = new int[][] { new int[] { 1, 2, 3 },
                                 new int[] { 5, 4, 3, 2, 1 },
                                 new int[] { 11, 22 } };

و در نهایت ساده‌ترین نوع نوشتاری کدهای فوق به صورت زیر است:

int[][] myArrays = { new int[] { 1, 2, 3 },
                     new int[] { 5, 4, 3, 2, 1 },
                     new int[] { 11, 22 } };

دسترسی به مقادیر آرایه‌های دندانه‌دار

برای دسترسی به عناصر هر یک از درایه‌های آرایه‌های دندانه‌دار به صورت زیر عمل می‌کنیم:

array[row][column]

یعنی با نوشتن دستور array[1][2] در واقع به آرایه‌ی سطر اول و ستون دوم دسترسی پیدا کرده‌ایم.

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

foreach(int[] array in myArrays)
{
   foreach(int number in array)
   {
      Console.WriteLine(number);
   }
}

توجه داشته باشید که آرایه‌ها را می‌توان به عنوان آرگومان به متدها یا توابع ارسال کرد. (در مورد متدها و توابع بعدا صحبت خواهیم کرد) به مثال زیر توجه کنید:

double getAverage(int[] arr, int size){

}

تابع getAverage به عنوان یک تابع بوده که مقدار بازگشتی آن به صورت double می‌باشد و آرگومان‌های دریافتی آن شامل یک آرایه یک بعدی به نام arr از نوع int و یک مقدار عددی به نام size‌ از نوع int‌ می‌باشد.

معرفی اجمالی ParamArrays

یک نوع دیگری در آرایه‌ها وجود دارد که به ParamArrays معروف هستند. هنگامیکه می‌خواهیم آرایه‌ای را به یک متد و یا تابع ارسال کنیم و از تعداد آرگومان‌های ارسالی به آن با خبر نیستیم از این نوع داده بهره می‌بریم. به مثال زیر توجه کنید:

using System;
namespace ArrayApplication
{
   class ParamArray
   {
      public int AddElements(params int[] arr)
      {
         int sum = 0;
         foreach (int i in arr)
         {
            sum += i;
         }
         return sum;
      }
   }
      
   class TestClass
   {
      static void Main(string[] args)
      {
         ParamArray app = new ParamArray();
         int sum = app.AddElements(512, 720, 250, 567, 889);
         Console.WriteLine("The sum is: {0}", sum);
         Console.ReadKey();
      }
   }
}

در این مثال در قسمت اصلی برنامه‌ (تابع main) در ابتدا یک داده آرایه‌ای از نوع ParamArray به نام app‌ ایجاد کرده‌ایم سپس در خط بعدی یک داده عددی به نام sum را ایجاد کرده که مقدار آن برابر مجموع تمام اعداد ارسالی به تابع AddElements‌ است. درنظر داشته باشید که این تابع به دلیل اینکه پارامترهای ورودی آن متغییر است آرگومانی از نوع []params int به نام arr‌ دریافت می‌کند. به عبارت دیگر مجموعه‌ای از عناصر متغییر را دریافت کرده و سپس مجموع آنها را باز می‌گرداند. (با مباحث توابع در آینده بیشتر آشنا می‌شوید.)

قبل از ورود به مبحث کلاس‌ها (Classes) که در آینده درباره آنها بیشتر صحبت خواهیم کرد لازم به ذکر دانستیم تا در این بخش کلاس آرایه‌ها و ویژگی‌های آنها را یادداشت کنیم تا با تدریس مبحث کلاس‌ها، مخاطب را جهت کار با آرایه‌ها به این قسمت ارجاع دهیم.

ویژگی‌ها (Properties)‌ کلاس آرایه

هر کلاس آرایه شامل یک سری ویژگی‌های اساسی است که به صورت معمول از آنها استفاده می‌شود. این ویژگی‌ها عبارتند از:

IsFixedSize: با استفاده از ویژگی مقدار سایز یک آرایه بازگردانده می‌شود.

IsReadOnly: با استفاده از این ویژگی بررسی می‌کنیم که این آرایه تنها قابل خواندن (بدون نوشتن) است یا خیر؟

Length: با استفاده از این ویژگی مشخص می‌شود که طول یک آرایه چقدر است. طول یک آرایه در واقع تعداد المان‌های موجود در تمام ابعاد است. مثلا طول یک آرایه دو بعدی با ۲ سطر و ۲ ستون برابر ۴ است.

Rank: رنک یک آرایه به معنای بعد آن است. مثلا رنک یک آرایه‌ی دو بعدی برابر ۲ و یک آرایه‌ی یک بعدی برابر ۱ است.

متدهای (Methods) کلاس آرایه

Clear: تمام المان‌های یک آرایه‌ را برابر ۰ می‌کند یا مقدار آنها را معادل false و Null قرار می‌دهد.

Copy(Array, Array, Int32): المان‌های یک آرایه را به آرایه‌ی دیگری کپی می‌کند که ایندکس آن از نوع integer 32-bit‌ است.

CopyTo(Array, Int32): تمام المان‌های آرایه‌ی یک بعدی جاری را به یک آرایه‌ی مشخص یک بعدی دیگر کپی می‌کند که ایندکس آن از نوع عدد صحیح ۳۲ بیتی است.

GetLength: طول المان‌های یک آرایه در یک بعد مشخص در اختیار قرار می‌دهد. (نوع داده‌ای که باز می‌گرداند از جنس عدد صحیح ۳۲ بیتی است)

GetLongLengh: طول المان‌های یک آرایه در یک بعد مشخص در اختیار قرار می‌دهد. (نوع داده‌ای که باز می‌گرداند از جنس عدد صحیح ۶۴ بیتی است)

GetLowerBound: کران پایین یک آرایه در یک بعد مشخص را در اختیار قرار می‌دهد. به عبارتی ایندکس شروع شده یک آرایه را از بعد اول آن باز می‌گرداند.

GetType: نوع نمونه‌ی آرایه‌ی جاری را که از کلاس Object به ارث برده شده است را باز می‌گرداند.

GetUpperBound: کران بالا یک آرایه در یک بعد مشخص را در اختیار قرار می‌دهد. به عبارتی ایندکس انتهایی یک آرایه را از بعد اول آن باز می‌گرداند.

GetValue(Int32): مقدار یک المان در موقعیت مشخصی در یک آرایه را در اختیار می‌گذارد که ایندکس آن بر اساس عدد صحیح ۳۲ بیتی معرفی می‌شود.

IndexOf(Array, Object): برای یک شیء Object‌ جستجویی درون آرایه‌ی Array انجام داده و در نهایت ایندکس اولین درایه‌ای که پیدا می‌کند را در اختیار قرار می‌دهد.

Reverse(Array):‌  المان‌های یک آرایه‌ را معکوس می‌کند، مثلا المان درایه‌ی ۰ را به درایه‌ی ۱۰ و المان درایه ۱۰ را به درایه ۰ انتقال می‌دهد.

SetValue(Object, Int32): مقدار یک المان در یک موقعیت مشخص را تنظیم می‌کند که ایندکس آن بر اساس عدد صحیح ۳۲ بیتی می‌باشد.

Sort(Array):‌ المان‌های یک آرایه‌ی یک بعدی را با استفاده از روش اجرای IComparable مرتب‌سازی می‌کند. (در بخشهای بعدی بیشتر در اینبار توضیح خواهیم داد)

ToStringk: شیء جاری را به رشته تبدیل کرده و نمایش می‌دهد.

برای نمایش نمونه‌ای موارد فوق به مثال زیر توجه کنید:

using System;
namespace ArrayApplication  
{
   class MyArray
   {
      static void Main(string[] args)
      {
         int[] list = { 34, 72, 13, 44, 25, 30, 10 };
         int[] temp = list;
         Console.Write("Original Array: ");
         
         foreach (int i in list)
         {
            Console.Write(i + " ");
         }
         Console.WriteLine();
         
         // reverse the array
         Array.Reverse(temp);
         Console.Write("Reversed Array: ");
         
         foreach (int i in temp)
         {
            Console.Write(i + " ");
         }
         Console.WriteLine();
         
         //sort the array
         Array.Sort(list);
         Console.Write("Sorted Array: ");
         
         foreach (int i in list)
         {
            Console.Write(i + " ");
         }
         Console.WriteLine();
         Console.ReadKey();
      }
   }
}

خروجی این مثال به صورت زیر است:

Original Array: 34 72 13 44 25 30 10
Reversed Array: 10 30 25 44 13 72 34
Sorted Array: 10 13 25 30 34 44 72

بسیار عالی به شما تبریک می‌گوییم. در این بخش توانستید تمام اطلاعات لازم در ارتباط با آرایه‌ها در فرا گرفته و مثال‌های مرتبط با آنها را بررسی کنید. حال می‌توانید با خیال آسوده وارد بخش بعدی شوید. آرایه‌ها به عنوان یکی از پرکاربردترین ساختارهای داده‌ها به حساب می‌آیند. قسمت آخر که مرتبط با کلاس آرایه‌ها است پس از معرفی فصل کلاس‌ها، برای شما ملموس تر خواهد بود. لذا در صورتیکه با خواندن آن دچار سرگیجه شده‌اید اصلا نترسید. :)

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش سی شارپ (C#) توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (1 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

محمد
19 مرداد 1398
ممنون از مطالب شما ، لطفا قبل از انتشار یک بار کدهارا آزمایش کنید و سپس منتشر کنید تا ابهامی برای خواننده وجود نداشته باشد . با تشکر

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.