Struct یا ساختار در سی شارپ

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

در این بخش به توضیح مفصل عبارتی تحت عنوان struct یا ساختارها می‌پردازیم. عناوینی که پس از پایان این فصل خواهید آموخت به صورت زیر می‌باشد:

  • مفهوم بنیادی struct ها یا ساختارها
  • پیاده‌‌سازی یک ساختار
  • نحوه‌ی استفاده از ساختار

struct یا ساختار چیست؟

ساختارها در واقع یک نوع مقدار هستند و برای ثبت داده استفاده می‌شوند. برای تفهیم بیشتر این موضوع بهتر است یک مقایسه‌ی کلیدی در ابتدای بحث با کلاس‌ها داشته باشیم. چنانچه مفاهیم کلاس‌ها در زبان برنامه‌نویسی #C را مطالعه نکرده‌اید مقاله‌ی زیر را مطالعه بفرمایید:

struct ها یا ساختارها به عنوان یک نوع مقدار شناخته می‌شوند در حالیکه کلاس‌ها تحت عنوان نوع ارجاعی یا reference type هستند. ساختارها فضایی در حافظه به خود اختصاص می‌دهند درحالیکه کلاس‌ها به یک شیء در حافظه رجوع می‌کنند.  ساختارها مفهومی به نام destructor یا مخرب ندارند در حالیکه کلاس‌ها از این قابلیت برخوردار هستند. اما این تفاوت‌ها به همین نقطه ختم نمی‌شود و یکی دیگر از تفاوت‌های ساختارها و کلاس‌ها این است که در ساختار ها مفهومی به نام ارث بری یا inheritance وجود ندارد در حالیکه کلاس‌ها به قدرت هر چه تمام‌تر از این مفهوم استفاده می‌کنند. در صورتیکه با مفهوم ارث‌بری آشنا نیستید مقاله‌ی زیر را مرور بفرمایید:

اما با این وجود یک شباهت بین هر دو وجود دارد که struct ها و کلاس ها هر دو از interface ها تبعیت می‌کنند. (واسط‌ یا Interface در فصل بعدی به تفصیل تدریس می‌شود)

بسیار عالی تا به اینجا شاید حدس زده باشید که struct ها بسیار شبیه به class ها هستند ولی تفاوت‌هایی بین این دو وجود دارد.

فریم ورک NET. انواع مختلفی از ساختارها یا struct ها را دارد که به صورت توکار ایجاد شده‌اند. برای مثال عبارت‌های System.Int32 یا System.single یا System.bool به عنوان ساختارهای توکار در سی شارپ استفاده می‌شوند. اگر شما به داکیومنت و مستندات شرکت ماکروسافت مراجعه کنید متوجه خواهید شد که داده‌های مثال قبل به عنوان structtype ها تعریف شده‌اند. در ادامه نحوه‌ی ایجاد یک struct را با یکدیگر بررسی خواهیم کرد.

چه زمانی از ساختارها استفاده می‌شود؟

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

  • قصد نمایش یک نوع داده‌ی مشخص را دارید (مانند int یا double)
  • اندازه‌ی سایز آن کمتر از ۱۶ بیت باشد
  • نباید تغییر پذیر باشد

ایجاد یک ساختار struct یا نوع داده‌ی دلخواه

علی‌رغم اینکه مفهوم ساختار و کلاس‌ با یکدیگر بسیار متفاوت است، اسکلت‌بندی آنها تقریبا شبیه هم می‌باشد. درون یک ساختار همانطور که در ادامه مشاهده خواهید کرد اعضا و ویژگی‌های متفاوتی وجود دارد. ساختارها با کلمه‌ی کلیدی struct ایجاد می‌شوند. در مثال زیر یک ساختار به نام Rectangle تعریف شده که دارای ویژگی‌هایی با نام‌های Width و Height است:

/// <summary>
/// Custom struct type, representing
    a rectangular shape
/// </summary>
struct Rectangle
{
    /// <summary>
    /// Backing Store for Width
    /// </summary>
    private int m_width;

    /// <summary>
    /// Width of rectangle
    /// </summary>
    public int Width 
    {
        get
        {
            return m_width;
        }
        set
        {
            m_width = value;
        }
    }

    /// <summary>
    /// Backing store for Height
    /// </summary>
    private int m_height;

    /// <summary>
    /// Height of rectangle
    /// </summary>
    public int Height
    {
        get
        {
            return m_height;
        }
        set
        {
            m_height = value;
        }
    }
}

با بررسی مثال فوق به این نکته پی می‌برید که ساختارها خیلی شبیه به classها هستند! ولی تفاوت‌های بنیادی دارند که در فوق به آنها اشاره کردیم.

استفاده از ساختارها یا struct ها

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

using System;

/// <summary>
/// Example of declaring and using
    a struct
/// </summary>
class StructExample
{
    /// <summary>
    /// Entry point: execution starts
        here
    /// </summary>
    static void Main()
    {
        // instantiate a new Rectangle struct
        // where Width is set to 1 and Height
            is set to 3
	Rectangle rect1 = new Rectangle();
        rect1.Width = 1;
        rect1.Height = 3;

        // show the value of Width and Height
            for rect1
        Console.WriteLine("rect1: {0}:{1}", rect1.Width, rect1.Height);

        Console.ReadKey();
    }
}

در قسمت متد اصلی این برنامه (Main) ابتدا یک شیء جدید از روی ساختار یا struct ای به نام Rectangle ایجاد و سپس ویژگی‌های width و height را مقدادهی کرده‌ایم. خروجی این مثال به صورت زیر خواهد بود:

rect1: 1:3

یک راه دیگر برای مقداردهی یک ساختار struct به صورت زیر است که در آن ویژگی‌ها را به هنگام تعریف یک شیء مقداردهی کنیم:

// you can also use object initialization syntax
Rectangle rect11 = new Rectangle
{
	Width = 1,
	Height = 3
};

توجه کنید که برای مقداردهی به صورت فوق به جای دو پرانتز () از یک جفت آکولارد { } استفاده کرده‌ایم.

سربارگیری یا Overloading سازنده‌های struct

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

/// <summary>
/// Custom struct type, representing
    a rectangular shape
/// </summary>
struct Rectangle
{
    /// <summary>
    /// Backing Store for Width
    /// </summary>
    private int m_width;

    /// <summary>
    /// Width of rectangle
    /// </summary>
    public int Width 
    {
        get
        {
            return m_width;
        }
        set
        {
            m_width = value;
        }
    }

    /// <summary>
    /// Backing store for Height
    /// </summary>
    private int m_height;

    /// <summary>
    /// Height of rectangle
    /// </summary>
    public int Height
    {
        get
        {
            return m_height;
        }
        set
        {
            m_height = value;
        }
    }

 /// <summary> /// Instantiate rectangle struct with
    dimensions /// </summary>
    /// <param name="width">Width
        to make new rectangle</param> 
            /// <param name="height">Height to make new rectangle</param>
    public Rectangle(int width, int height) 
        { 
            m_width = width;
            m_height = height; 
        }
}

همانطور که ملاحظه می‌فرمایید یک سازنده دلخواه تحت عنوان Rectangle با پارامترهای ورودی width و height نوشته‌ایم.

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

using System;

/// <summary>
/// Example of declaring and using
    a struct
/// </summary>
class StructExample
{
    /// <summary>
    /// Entry point: execution starts
        here
    /// </summary>
    static void Main()
    {
        // instantiate a new Rectangle struct
        // where Width is set to 5 and Height
            is set to 7
	Rectangle rect2 = new Rectangle(5, 7);

        // show the value of Width and Height
            for rect2
        Console.WriteLine("rect2: {0}:{1}", rect2.Width, rect2.Height);

        Console.ReadKey();
    }
}

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

rect2: 5:7

اضافه کردن یک متد به یک struct

دقیقا مشابه کلاس‌ها می‌توان متدهای متنوعی برای یک ساختار تعریف کرد که در مجموعه‌ی دستورهای زیر متدی تحت عنوان Add را به مثال فوق اضافه کرده‌ایم:

/// <summary>
/// Custom struct type, representing
    a rectangular shape
/// </summary>
struct Rectangle
{
    /// <summary>
    /// Backing Store for Width
    /// </summary>
    private int m_width;

    /// <summary>
    /// Width of rectangle
    /// </summary>
    public int Width 
    {
        get
        {
            return m_width;
        }
        set
        {
            m_width = value;
        }
    }

    /// <summary>
    /// Backing store for Height
    /// </summary>
    private int m_height;

    /// <summary>
    /// Height of rectangle
    /// </summary>
    public int Height
    {
        get
        {
            return m_height;
        }
        set
        {
            m_height = value;
        }
    }

    /// <summary>
    /// Instantiate rectangle struct
        with dimensions
    /// </summary>
    /// <param name="width">Width
        to make new rectangle</param>
    /// <param name="height">Height
        to make new rectangle</param>
    public Rectangle(int width, int height)
    {
        m_width = width;
        m_height = height;
    }


    /// <summary> 
    /// Increase the size of this rectangle by the size of the specified rectangle
    /// </summary>
    /// <param name="rect">Rectangle that will be added to this rectangle</param>
    /// <returns>New rectangle created by adding rect to this rectangle</returns>
    public Rectangle Add(Rectangle rect)
    { 
        // create instance of rectangle struct with default constructor
        Rectangle newRect = new Rectangle();

        // add matching axes and assign to new Rectangle struct
        newRect.Width = Width + rect.Width; newRect.Height = Height + rect.Height;

        // return new Rectangle struct
        return newRect; 
    }
}

و در نهایت برای فراخوانی این متد روش زیر را در پی می‌گیریم:

using System;

/// <summary>
/// Example of declaring and using
    a struct
/// </summary>
class StructExample
{
    /// <summary>
    /// Entry point: execution starts
        here
    /// </summary>
    static void Main()
    {
        // instantiate a new Rectangle struct
        // where Width is set to 1 and Height is set to 3
	Rectangle rect1 = new Rectangle();
        rect1.Width = 1;
        rect1.Height = 3;

        // show the value of Width and Height for rect1
        Console.WriteLine("rect1: {0}:{1}", rect1.Width, rect1.Height);

        // instantiate a new Rectangle struct
        // where Width is set to 5 and Height is set to 7
	Rectangle rect2 = new Rectangle(5, 7);

        // show the value of Width and Height for rect2
        Console.WriteLine("rect2: {0}:{1}", rect2.Width, rect2.Height);


        // invoke the Add method on the rect1 Rectangle struct instance,
        // passing the rect2 Rectangle struct instance as an argument
        // and assigning the new copy of the value returned by the
        // Add method to the rect3 Rectangle struct.
        Rectangle rect3 = rect1.Add(rect2);

        // show the value of Width and Height for rect3
        Console.WriteLine("rect3: {0}:{1}", rect3.Width, rect3.Height);

        Console.ReadKey();
   }
}

خروجی نهایی ما به صورت زیر می‌باشد؛

rect1: 1:3 
rect2: 5:7 
rect3: 6:10

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

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

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