مروری پیشرفته بر آرایه‌ها در سی شارپ

مروری پیشرفته بر آرایه‌ها در سی شارپ

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

آرایه چیست؟

آرایه (Array) مجموعه‌ای از عناصر یا به زبان ساده‌تر آیتم‌های داده‌ای است که دسترسی به آن‌ها از طریق یک شاخص عددی انجام می‌شود. به طور خاص، آرایه مجموعه‌ای از نقاط داده‌ی به هم پیوسته از نوع یکسان است. به عنوان مثال، آرایه‌ای از نوع اعداد صحیح (int)، آرایه‌ای از رشته‌ها (Strings) و غیره همگی نوع‌های یکسانی را نگه‌داری می‌کنند. 
در سی شارپ - #C ، یک آرایه می‌تواند با استفاده از یک نوع داده‌ای همراه با عملگرهای [] تعریف شود. دستورهای زیر همگی نحوه‌ی تعریف آرایه‌ها در سی شارپ را نشان می‌دهند. به ترتیب دستورهای زیر، آرایه‌هایی از نوع صحیح، بولین، رشته‌ای و اعشاری را تعریف می‌کنند. 

int[] intArray;  
bool[] boolArray;
string[] stringArray; 
double[] doubleArray; 

در #C هنگامی که قصد داریم آرایه‌ای ایجاد کرده و آن را در هنگام تعریف مقداردهی اولیه کنیم از کلمه کلیدی new استفاده می‌کنیم.

int[] intArray1 = new int [3];

لازم به توضیح است که کلمه‌ی کلیدی new در سی شارپ را می‌توان به شکل عملگر (operator)، اصلاح‌کننده (modifier) یا  قید (constraint) مورد استفاده قرار داد.. 

فرآیند تعریف، مقداردهی و دسترسی به یک آرایه در سی شارپ کاملا ساده است. برای درک بهتر مسئله به شبه کد زیر دقت کنید (برای آزمایش کدهای زیر کافی است محیط Microsoft Visual Studio یا Visual Studio Code را اجرا کنید، یک پروژه از نوع Console ایجاد کنید). 

Sokanacademy();
Console.ReadLine();
static void Sokanacademy()
{
    Console.WriteLine("=> Create a simple array.");
    // ساخت و مقداردهی آرایه با سه مقدار صحیح
    int[] myInts = new int[3];
    // ساخت یک آرایه رشته‌ای با قابلیت دریافت 100 عنصر از 0 تا 99
    string[] StringArrays = new string[100];
   Console.WriteLine();
}

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

     int[] myInts = new int[3]

در اصل آرایه‌ای را تعریف کرده‌ایم که سه عنصر در موقعیت مکانی 0، 1 و 2 ذخیره شده و مورد دسترسی قرار می‌گیرند. به بیان دیگر، در زبان‌های برنامه‌نویسی مثل سی شارپ، آرایه‌ها با صفر و نه یک آغاز می‌شوند. 

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

static void Sokanacademy()
{
    Console.WriteLine("=> Create a simple array.");
    // ساخت و مقداردهی آرایه با سه مقدار صحیح
    int[] myInts = new int[3];
    myInts[0] = 100;
    myInts[1] = 200;
    myInts[2] = 300;
    // نمایش مقادیر آرایه با حلقه
    foreach (int i in myInts)
    {
       Console.WriteLine(i);
    }
   Console.WriteLine();
}

خروجی قطعه کد بالا در تصویر زیر نشان داده شده است:

نکته‌ای که متاسفانه برخی از برنامه‌نویسان اطلاعی در مورد آن ندارند این است که هنگامی که آرایهای را تعریف می‌کنند، اما به طور صریح اقدام به مقداردهی آن نمی‌کنند، آرایه با مقدار پیش‌فرض آن نوع داده‌ای، مقداردهی اولیه می‌شود. به‌طور مثال، آرایه‌ای از نوع bools با مقدار false یا آرایه‌ای از نوع Integer با 0 مقداردهی اولیه می‌شود.

ترکیب نحوی مقداردهی اولیه آرایه‌ها 

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

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

Sokanacademy2();

static void Sokanacademy2()
{
    // new تعریف و مقداردهی اولیه آرایه با استفاده از کلمه کلیدی
    string[] MyString = new string[]
    { "Tehran", "Isfahan", "Shiraz" };
    Console.WriteLine("MyString has {0} elements", MyString.Length);
    // new تعریف و مقداردهی اولیه آرایه بدون استفاده از کلمه کلیدی
    bool[] booleanArray = { false, false, true };
    Console.WriteLine("booleanArray has {0} elements", booleanArray.Length);
    //و تعیین اندازه آرایه new تعریف و مقداردهی اولیه آرایه با استفاده از کلمه کلیدی
    int[] integerArray = new int[4] { 10, 20, 30, 40 };
    Console.WriteLine("integerArray has {0} elements", integerArray.Length);
   Console.WriteLine();

    foreach( string str in MyString)
    {
       Console.WriteLine(str);
    }
}

خروجی قطعه کد بالا در شکل زیر نشان داده شده است:

در کد بالا، نکته‌ای که باید به آن دقت کنید این است که وقتی از الگوی دستوری {} استفاده می‌کنید، نیازی به تعیین اندازه‌ی آرایه ندارید (درست مشابه حالتی که آرایه MyStrings را تعریف کردیم)، زیرا کامپایلر، تعداد عناصری که آرایه قادر به دریافت آن‌ها است را از تعداد آیتمهای موجود در محدوده‌ی تعریف شده محاسبه می‌کند. همچنین، استفاده از کلمه‌ی کلیدی new جنبه اختیاری دارد، مشابه حالتی که ما در ارتباط با آرایه‌ی BooleanArray از آن استفاده کردیم. 

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

int[] integerArray = new int[4] { 10, 20, 30, 40,50 };

در چنین شرایطی کامپایلر پیغام خطای زیر را نشان می‌دهد. 

An array initializer of length '4' is expected

آشنایی با آرایههای Implicitly Typed Local

کلمه‌ی کلیدی var، به شما اجازه می‌‌دهد تا متغیری را تعریف کنید که نوع اصلی آن توسط کامپایلر حدس زده می‌شود. ما می‌توانیم از این تکنیک برای تعریف متغیرها در سی‌شارپ استفاده کنیم، اما امکان استفاده از آن برای تعریف نوع خاصی از آرایه‌ها که آرایههای Implicitly Typed Local نام دارند، قابل استفاده است. با استفاده از این تکنیک، می‌توانید یک آرایهی جدید را بدون تعیین نوع آرایه تعریف کنید. هنگام استفاده از این تکنیک، باید از کلمه‌ی کلیدی new استفاده کنید.

static void ImplicitArrays()
{
    Console.WriteLine("=> Implicit Array.");
    // متغیری از نوع صحیح است  a
    var a = new[] { 10, 20, 30, 40 };
    Console.WriteLine("a is a: {0}", a.ToString());
    // متغیری از نوع اعشاری است  b
    var b = new[] { 1, 1.5, 2, 2.5 };
    Console.WriteLine("b is a: {0}", b.ToString());
    // متغیری از نوع رشته ای است  c
    var c = new[] { " sokan", null, "academy" };
    Console.WriteLine("c is a: {0}", c.ToString());
   Console.WriteLine();
}

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

var d = new[] { 1, "one", 2, "two", false };

کامپایلر در زمان ارزیابی کدها، پیغام خطایی مبنی بر ناسازگاری مقادیر درون آرایه نشان می‌دهد.

تعریف آرایه‌ای از اشیا

در بیشتر موارد، زمانی که یک آرایه را تعریف می‌کنید، این کار را با تعیین نوع صریح آرایه انجام می‌دهید. در حالی که این مسئله در ظاهر عادی به نظر می‌رسد، اما نکته ظریفی وجود دارد که باید به آن دقت کنید. اگر تجربه‌ی کدنویسی به زبان سی‌شارپ را داشته باشید به خوبی از این نکته اطلاع دارید که System.Object کلاس پایه برای نوع‌های داده‌ای (از جمله انواع داده‌های اساسی) در فریمورک NET Core است. 

با توجه به این واقعیت، اگر بخواهید آرایهای از نوع‌های داده‌‌ای که System.Object میزبانی می‌کند را تعریف کنید، باید به برخی ملاحظات دقت کنید. برای روشن شدن بحث به متد MyArray زیر دقت کنید:

static void MyArray()
{
    // آرایه ای از اشیا که می تواند هر نوع داده ای را داشته باشد
    object[] myItems = new object[4];
    myItems[0] = 10;
    myItems[1] = false;
    myItems[2] = new DateTime(2022, 9, 19);
    myItems[3] = " Sokan & Academy";
    foreach (object obj in myItems)
    {
        // چاپ مقدار و نوع عناصر درج شده در آرایه
       Console.WriteLine("Type: {0}, Value: {1}", obj.GetType(), obj);
    }
   Console.WriteLine();
}

خروجی قطعه کد بالا در شکل زیر نشان داده شده است:

در قطعه کد بالا، همزمان با چاپ مقادیر آرایه MyItems، نوع اصلی هر یک از آیتمها را با استفاده از متد GetType() System.Object چاپ می‌کنیم. بدون آن‌که وارد جزییات مربوط به متد System.Object.GetType شویم، به طور اجمالی باید به این نکته اشاره می‌کنیم که متد فوق روشی برای یافتن نوع داده‌ها ارائه می‌دهد 

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

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

static void MultidimArray()
{
    // یک آرایه چند بعدی به نام ماتریکس
    int[,] Matrix;
Matrix = new int[5, 6];
    // مقداردهی آرایه 6*5
    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 6; j++)
        {
            Matrix[i, j] = i * j;
        }
    }
    // چاپ آرایه 6*5
    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 6; j++)
        {
           Console.Write(Matrix[i, j] + "\t");
        }
       Console.WriteLine();
    }
    Console.WriteLine();
}

خروجی قطعه کد فوق به شرح زیر است:

دومین نوع آرایهها، به نام آرایه‌های دنباله‌دار معروف هستند و در زبان انگلیسی به‌نام Jagged Array معروف هستند. همان‌گونه که ممکن است حدس زده باشید، آرایههای دنباله‌دار شامل تعدادی آرایهی داخلی هستند که هر کدام ممکن است، اندازه متفاوتی داشته باشند. برای روشن شدن بحث، به مثال زیر دقت کنید: 

static void JaggedArray()
{
    // در قطعه کد زیر ما پنج آرایه مختلف داریم
    int[][] myJag = new int[5][];
    // فرآیند ساخت آرایه‌های دنباله‌دار به صورت زیر است
    for (int i = 0; i < myJag.Length; i++)
    {
        myJag [i] = new int[i + 7];
    }
    // فرآیند چاپ عناصر آرایه به شرح زیر است. دقت کنید که مقدار پیش‌فرض هر عنصر برابر با صفر است
    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < myJag[i].Length; j++)
        {
           Console.Write(myJag[i][j] + " ");
        }
       Console.WriteLine();
    }
   Console.WriteLine();
}

خروجی قطعه کد بالا به شرح زیر است:

چگونه از یک آرایه به عنوان آرگومان یا پارامتر خروجی استفاده کنیم؟

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

static void ShowArray(int[] myInteger)
{
    for (int i = 0; i < myInteger.Length; i++)
    {
        Console.WriteLine("Item {0} is {1}", i, myInteger[i]);
    }
}

static string[] GetStringArray()
{
    string[] myStrings = { "Sokan", "Akademy", "GetStringArray" };
    return myStrings;
}

فرآیند دریافت و خواندن آرایه‌ها به عنوان آرگومان ورودی یا خروجی به شرح زیر است:

static void SetAndGetArrays()
{
    int[] myintegers = { 20, 22, 23, 0 };
   ShowArray(myintegers);
    // آرایه به عنوان مقدار خروجی بازگردانده می‌شود
    string[] strs = GetStringArray();
    foreach (string s in strs)
    {
       Console.WriteLine(s);
    }
   Console.WriteLine();
}
SetAndGetArrays();

خروجی قطعه کد بالا در شکل زیر نشان داده شده است:

کلاس پایه System.Array Base

تقریبا بیشتر آرایه‌هایی که در زبان سی‌شارپ از آن‌ها استفاده می‌کنیم، زیرمجمموعه کلاس System.Array هستند. به بیان دقیق‌تر، این آرایه‌ها، متدهایی که کلاس Array در اختیار آن‌ها قرار می‌دهد را مورد استفاده قرار می‌دهند. به طور معمول، در دنیای برنامه‌نویسی حرفه‌ای و کار با آرایه‌ها در سی‌شارپ از توابعی مثل ()Clear()، CopyTo()، Lenght()، Reverse و ()Sort برای انجام عملیات مختلف روی آرایه‌ها استفاده می‌کنیم. 

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

static void ArraysMethods()
{
    // مقداردهی اولیه عناصر آرایه
    string[] MyArray = { "hamid reza taebi", " sokanacademy", "Learning C#" };
    // نمایش عناصر آرایه به ترتیبی که درج شده‌اند
    Console.WriteLine("-> My array:");
    for (int i = 0; i < MyArray.Length; i++)
    {
        // نمایش اسامی
        Console.Write(MyArray[i] + ", ");
    }
    Console.WriteLine("\n");
    // معکوس کردن عناصر
   Array.Reverse(MyArray);
    Console.WriteLine("-> The reversed array");
    // نمایش عناصر بعد از معکوس شدن
    for (int i = 0; i < MyArray.Length; i++)
    {
        // نمایش اسامی
       Console.Write(MyArray[i] + ", ");
    }
    Console.WriteLine("\n");
    // پاک کردن تمام عناصر به غیر از مورد اول
    Console.WriteLine("-> Cleared all elements, except one...");
   Array.Clear(MyArray, 1, 2);
    for (int i = 0; i < MyArray.Length; i++)
    {
        // نمایش اسامی
       Console.Write(MyArray[i] + ", ");
    }
   Console.WriteLine();
}

خروجی قطعه کد بالا در شکل زیر نشان داده شده است:

نکته‌ای که باید به عنوان یک برنامه‌نویس به آن دقت کنید این است که بیشتر متدهای کلاس System.Array در قالب آیتم‌های ایستا تعریف می‌شوند و به نام توابع کلاس نامیده می‌شوند، مثل ()Array.Sort()، Array.Reverse و نمونه‌های مشابه. 

هنگامی که آرایه‌ای در سی شارپ تعریف می‌کنید، متدهایی مثل موارد یاد شده، به شکل خودکار به تعریف آرایه ضمیمه می‌‌شوند و بدون مشکل قادر به استفاده از آن‌ها هستید. سایر اعضا کلاس System.Arrayمثل Length ماهیت ایستا ندارند و در سطح شی قابل استفاده هستند. این نوع اعضا نیز، به شکل مستقیم روی آرایه‌ها قابل استفاده هستند، درست به همان روشی که در قطعه کد بالا از آن‌ها استفاده کردیم.

کلام آخر

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

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

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


online-support-icon