دسته بندی وبلاگ

ساختارها در CSharp سی شارپ


در اين قسمت با ساختارها (Struct) در زبان #C آشنا مي‌شويم.

  اهداف اين قسمت بشرح زير مي‌باشند:
• يك struct يا ساختار (Structure) چيست؟
• پياده‌سازي ساختارها(Struct)
• استفاده از ساختارها(Struct)
• نكات مهم و مطالب كمكي درباره struct ها

  ساختار (struct) چيست؟
همانطور كه با استفاده از كلاسها مي‌توان انواع (types) جديد و مورد نظر را ايجاد نمود، با استفاده از struct ها مي‌توان انواع مقداري (value types) جديد و مورد نظر را ايجاد نمود. از آنجائيكه struct ها بعنوان انواع مقداري در نظر گرفته مي‌شوند، از اينرو تمامي اعمال مورد استفاده بر روي انواع مقداري را مي‌توان براي struct ها در نظر گرفت. struct ها بسيار شبيه به كلاس‌ها هستند و مي‌توانند داراي فيلد، متد و property باشند. عموماً ساختارها مجموعه كوچكي از عناصري هستند كه منطقي با يكديگر داراي رابطه مي‌باشند. براي نمونه مي‌توان به ساختار Point موجود در Framework SDK اشاره كرد كه حاوي دو property با نامهاي X و Y است.

با استفاده از ساختارها (struct) مي‌توان اشيايي با انواع جديد ايجاد كرد كه اين اشياء مي‌توانند شبيه به انواع موجود (int, float, …) باشند. حال سوال اينست كه چه زماني از ساختارها(struct) بجاي كلاس استفاده مي‌كنيم؟ در ابتدا به نحوه استفاده از انواع موجود در زبان ‍C# توجه نماييد. اين انواع داراي مقادير و عملگرهاي معيني جهت كار با اين مقادير هستند. حال اگر نياز به شي‌اي داريد كه همانند اين انواع رفتار نمايند لازم است تا از ساختارها (struct) استفاده نماييد. در ادامه اين مبحث نكات و قوانيني را ذكر مي‌كنيم كه با استفاده از آنها بهتر بتوانيد از ساختارها (struct) استفاده نماييد.

اعلان و پياده‌سازي struct
براي اعلان يك struct كافيست تا با استفاده از كلمه كليدي struct كه بدنبال آن نام مورد نظر براي ساختار آمده استفاده كرد. بدنة ساختار نيز بين دو كروشة باز و بسته {} قرار خواهد گرفت. به مثال زير توجه نماييد :

مثال 1-12 : نمونه‌اي از يك ساختار (Struct)

كد:

using System;
struct Point
{
public int x;
public int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
public Point Add(Point pt)
{
Point newPt;
newPt.x = x + pt.x;
newPt.y = y + pt.y;
return newPt;
}
}
/// <summary>
/// struct مثالي از اعلان و ساخت يك
/// </summary>
class StructExample
{
static void Main(string[] args)
{
Point pt1 = new Point(1, 1);
Point pt2 = new Point(2, 2);
Point pt3;
pt3 = pt1.Add(pt2);
Console.WriteLine("pt3: {0}:{1}", pt3.x, pt3.y);
}
}



مثال 1-12 نحوة ايجاد و استفاده از struct را نشان مي‌دهد. به راحتي مي‌توان گفت كه يك نوع(type) ، يك struct است، زيرا از كلمه كليدي struct در اعلان خود بهره مي‌گيرد. ساختار پايه‌اي يك ساختار پايه‌اي يك struct بسيار شبيه به يك كلاس است، ولي تفاوتهايي با آن دارد كه اين تفاوتها در پاراگراف بعدي مورد بررسي قرار مي‌گيرند. ساختار Point داراي سازنده ايست كه مقادير داده شده با آنرا به فيلدهاي x و y تخصيص مي‌دهد. اين ساختار همچنين داراي متد Add() مي‌باشد كه ساختار Point ديگري را دريافت مي‌كند و آنرا به struct كنوني مي‌افزايد و سپس struct جديدي را باز مي‌گرداند.

توجه نماييد كه ساختار Point جديدي درون متد Add() تعريف شده است. توجه كنيد كه در اينجا همانند كلاس، نيازي به استفاده از كلمه كليدي new جهن ايجاد يك شيء جديد نمي‌باشد. پس از آنكه نمونة جديدي از يك ساختار ايجاد شد، سازندة پيش فرض (يا همان سازندة بدون پارامترش) براي آن در نظر گرفته مي‌شود. سازندة بدون پارامتر كليه مقادير فيلدهاي ساختار را به مقادير پيش فرض تغيير مي‌دهد. بعنوان مثال فيلدهاي صحيح به صفر و فيلدهاي Boolean به false تغيير مي‌كنند. تعريف سازندة بدون پارامتر براي يك ساختار صحيح نمي‌باشد. (يعني شما نمي‌توانيد سازندة بدون پارامتر براي يك struct تعريف كنيد.)

ساختارها (structs) با استفاده از عملگر new نيز قابل نمونه‌گيري هستند (هر چند نيازي به استفاده از اين عملگر نيست.) در مثال 1-12 pt1 و pt2 كه ساختارهايي از نوع Point هستند، با استفاده از سازندة موجود درون ساختار Point مقداردهي مي‌شوند. سومين ساختار از نوع Point، pt3 است و از سازندة بدون پارامتر استفاده مي‌كند زيرا در اينجا مقدار آن اهميتي ندارد. سپس متد Add() از ساختار pt1 فراخوانده مي‌شود و ساختار pt2 را بعنوان پارامتر دريافت مي‌كند. نتيجه به pt3 تخصيص داده مي‌شود، اين امر نشان مي‌دهد كه يك ساختار مي‌تواند همانند ساير انواع مقداري مورد استفاده قرار گيرد. خروجي مثال 1-12 در زير نشان داده شده است :
pt3 : 3 : 3

يكي ديگر از تفاوتهاي ساختار و كلاس در اينست كه ساختارها نمي‌توانند داراي تخريب كننده (deconstructor) باشند. همچنين ارث‌بري در مورد ساختارها معني ندارد. البته امكان ارث‌بري بين ساختارها و interface ها وجود دارد. يك interface نوع مرجعي زبان C# است كه داراي اعضايي بدون پياده‌سازي است. هر كلاس و يا ساختاري كه از يك interface ارث‌بري نمايد بايد تمامي متدهاي آنرا پياده‌سازي كند. دربارة interface ها در آينده صحبت خواهيم كرد.

خلاصه :
هم اكنون شما با چگونگي ايجاد يك ساختار آشنا شديد. هنگاميكه قصد داريد نوعي را بصورت ساختار يا كلاس پياده‌سازي كنيد، بايد به اين نكته توجه كنيد كه اين نوع چگونه مورد استفاده قرار مي‌گيرد. اگر مي‌خواهيد سازنده‌اي بدون پارامتر داشته باشيد، در اينصورت كلاس تنها گزينه شماست. همچنين توجه نماييد از آنجائيكه يك ساختار بعنوان يك نوع مقداري در نظر گرفته مي‌شود، در پشته (Stack) ذخيره مي‌شود و حال آنكه كلاس در heap ذخيره مي‌گردد.

نكات مهم و مطالب كمكي

1. تفاوتهاي اصلي بين كلاس و ساختار در چيست؟
همانطور كه بطور مختصر در بالا نيز اشاره شد، از نظر نوشتاري (syntax) struct و كلاس بسيار شبيه به يكديگر هستند اما داراي تفاوتهاي بسيار مهمي با يكديگر مي‌باشند.
همانطور كه قبلاً نيز اشاره شد شما نمي‌توانيد براي يك struct سازند‌ه‌اي تعريف كنيد كه بدون پارامتر است، يعني براي ايجاد سازنده براي يك struct حتماً بايد اين سازنده داراي پارامتر باشد. به قطعه كد زير توجه كنيد :

كد:

struct Time
{
    public Time() { ... } // خطاي زمان كامپايل رخ مي‌دهد
   ⋮
}

پس از اجراي كد فوق كامپايلر خطايي را ايجاد خواهد كرد بدين عنوان كه سازندة struct حتماٌ بايد داراي پارامتر باشد. حال اگر بجاي struct از كلمه كليدي calss استفاده كرده بوديم اين كد خطايي را ايجاد نمي‌كرد. در حقيقت تفاوت در اينست كه در مورد struct، كامپايلر اجازة ايجاد سازندة پيش فرض جديدي را به شما نمي‌دهد ولي در مورد كلاس چنين نيست. هنگام اعلان كلاس در صورتيكه شما سازندة پيش فرضي اعلان نكرده باشيد، كامپايلر سازنده‌اي پيش فرض براي آن در نظر مي‌گيرد ولي در مورد struct تنها سازندة پيش فرضي معتبر است كه كامپايلر آنرا ايجاد نمايد نه شما !
يكي ديگر از تفاوتهاي بين كلاس و struct در آن است كه، اگر در كلاس برخي از فيلدهاي موجود در سازندة كلاس را مقداردهي نكنيد، كامپايلر مقدار پيش فرض صفر، false و يا null را براي آن فيلد در نظر خواهد گرفت ولي در struct تمامي فيلدهاي سازنده بايد بطور صريح مقداردهي شوند و درصورتيكه شما فيلدي را مقداردهي نكيد كامپايلر هيچ مقداري را براي آن در نظر نخواهدگرفت و خطاي زمان كامپايل رخ خواهد داد. بعنوان مثال در كد زير اگر Time بصورت كلاس تعريف شده بود خطايي رخ نمي‌داد ولي چون بصورت struct تعريف شده خطاي زمان كامپايل رخ خواهد داد :

كد:

struct Time
{
    public Time(int hh, int mm)
    {
        hours = hh;
        minutes = mm;
    }   // خطاي زمان كامپايلي بدين صورت رخ مي‌دهد : seconds not initialized
   ⋮
    private int hours, minutes, seconds;
}



تفاوت ديگر كلاس و struct در اينست كه در كلاس مي‌توانيد در هنگام اعلان فيلدها را مقداردهي كنيد حال آنكه در struct چنين عملي باعث ايجاد خطاي زمان كامپايل خواهد شد. همانند كدهاي فوق، در كد زير اگر از كلاس بجاي struct استفاده شده بود خطا رخ نمي‌داد :

كد:

struct Time
{
   ⋮
    private int hours = 0; // خطاي زمان كامپايل رخ مي‌دهد
    private int minutes;
    private int seconds;
}



آخرين تفاوت بين كلاس و struct كه ما به آن خواهيم پرداخت در مورد ارث‌بري است. كلاسها مي‌توانند از كلاس پاية خود ارث‌بري داشته باشند در حاليكه ارث‌بري در struct ها معنايي ندارد و يك struct تنها مي‌تواند از واسطها (interface) ارث‌بري داشته باشد.

2. پس از ايجاد يك ساختار چگونه مي‌توان از آن استفاده نمود؟
همانطور كه گفتيم، ساختارها روشي براي ايجاد انواع جديد مقدار (Value Types) هستند. از اينرو پس از ايجاد يك ساختار مي‌توان از آن همانند ساير انواع مقداري استفاده نمود. براي استفاده از يك ساختار ايجاد شده كافيست تا نام آنرا قبل از متغير مورد نظر قرار دهيم تا متغير مورد نظر از نوع آن ساختار خاص تعريف شود.

كد:

struct Time
{
   ⋮
    private int hours, minutes, seconds;
}
 
class Example
{
    public void Method(Time parameter)
    {
        Time localVariable;
       ⋮
    }
    private Time field;
}



آخرين نكته‌اي كه در مورد ساختارها براي چندمين بار اشاره مي‌كنم انست كه، ساختارها انواع مقداري هستند و مستقيماً مقدار را در خود نگه مي‌دارند و از اينرو در stack نگه‌داري مي‌شوند. استفاده از ساختارها همانند ساير انواع مقداري است.