تفاوت Value type و Reference type

طبق انواع DataType های زبان C# دو تا از مهمترین اونا Value type ها و Reference typeها هستند که با نحوه ذخیره سازی در حافظه تفاوت پیدا میکنند. به طوری که Value Typeها در Stack ذخیره میشوند ولی Reference Typeها در Stack و Heap. اما یعنی چی؟

 

 

بذارید یه چیزی رو تعریف کنم، رفته بودم برای مصاحبه و ازم پرسیدند که فرق بین Value type و Reference type در زبان C# چیه منم که جمله بالا رو خوب تو ذهنم بود سریع گفتم و با مثال زدن Struct و Class تفاوت هارو گفتم ولی در عمل ازم خواستند تا مثالی رو حل کنم ولی خب نتونستم جواب بدم. خیلی برام جالب شد و رفتم اساسی راجع بهش خوندم و به نتایجی رسیدم که دیدم حیفه اینجا به اشتراک نذارم.

 

1) Value Type:

این نوع متغیرها مقدار خودشون رو به همراه فضای حافظه ذخیره میکنند. یعنی متغیر به صورت مستقیم و بدون واسطه مقدارش رو حفظ کرده.

 

مثلا این متغیر Integer:

int i = 100;

 

سیستم مقدار 100 رو در حافظه رو دقیقن با i ذخیره میکنه، عکس پایین نشون میده که مثلا خونه حافظه 0x239110 به طور فرضی اختصاص پیدا کرده به متغیر i: 

انواع Value type ها:

  • bool
  • byte
  • char
  • decimal
  • double
  • enum
  • float
  • int
  • long
  • sbyte
  • short
  • struct
  • uint
  • ulong
  • ushort

 

مقدار دهی کردن Value type ها:

وقتی که مقدار یک متغیر Value Type رو به یک متد دیگر پاس میدهیم، در سیستم یه نسخه جدا از متغیر در اون متد خاص ساخته میشه. بنابراین اگر مقدار در اون متد خاص متغیر تغییر کنه تاثیری روی مقدار متغیر در متد دیگه ای نخواهد داشت، مثال زیر رو ببینید:

static void ChangeValue(int x)
{
    x =  200;

    Console.WriteLine(x);
}

static void Main(string[] args)
{
    int i = 100;

    Console.WriteLine(i);
    
    ChangeValue(i);
    
    Console.WriteLine(i);
}

خروجی:

100 
200 
100

همانطور که میبینید متغیر i در متد Main مقدارش تغییری نکرد حتی بعد از اینکه ما مقدارش رو پاس دادیم به متد ChangeValue و در اون متد تغییرش دادیم.

 

2) Reference Type:

خب برخلافه Value Type ها که توضیح دادیم، Reference Type ها مقدار رو مستقیم در خودشون ذخیره نمیکنند. به جاش آدرس خونه حافظه ای که در اونجا، مقدار ذخیره شده، رو نگه میدارند. بذار یه جور دیگه بگیم اگه بحث اشاره‌گرها در زبان C رو یادتون باشه، در واقع یک Reference Type یه اشاره گری در خودش داره که این اشاره گر حاوی آدرس خونه ای از حافظه‌س که مقدار در اون نگه داری شده.

stringها از جنس Reference Typeها هستند، مثال رو ببینید:

string s = "Hello World!!";

 

اینجا چه اتفاقی میوفته، در واقع سیستم مقدار نسبت داده شده رو در یک خانه حافظه ذخیره میکنه و بعد یک خونه حافظه رو اختصاص میده به اسم متغیر و در اون آدرس مقدار متغیر رو نگه میداره، عکس زیر رو ببینید:

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

 

انواع Reference type ها:

  • String
  • همه arrayها, حتی وقتی عناصر تشکلیل دهندشون value type باشند
  • Class
  • Delegates

 

مقدار دهی کردن Reference type ها:

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

static void ChangeReferenceType(Student std2)
{
    std2.StudentName = "Steve";
}

static void Main(string[] args)
{
    Student std1 = new Student();
    std1.StudentName = "Bill";
    
    ChangeReferenceType(std1);

    Console.WriteLine(std1.StudentName);
}

خروجی:

Steve

 

در مثالی که دیدید، زمانی که ما یک نسخه از Student رو با نام std1 به متد ChangeReferenceType میفرستیم، اتفاقی که میوفته اینه که آدرس خانه حافظه std1 ارسال میشه. بدین ترتیب وقتی که متد StudentName رو تغییر میده در کلاس اصلی ما هم نام تغییر میکنه. چرا؟ چون std1 و std2 دارند هر دو به یک خونه حافظه اشاره میکنند و یه جورایی میشه گفت هردوشون یکی هستند. بنابراین خروجی ما Steve خواهد بود.

 

null value:

Reference type ها یکی از خاصیتهایی که دارند اینه که اگه شما به اونهارو مقدار دهی نکنید به صورت پیش فرض مقدار Null یا تهی دارند. بذارید یه مثال بزنم، یک متغیر از جنس رشته یا string (میتونه هر نوع دیگه‌ای از Reference Typeها باشه) وقتی که مقدار دهی نشه مقدار Null درش قرار میگیره که این به معنیه که دیگه به هیچ خونه ای از حافظه اشاره نمیکنه و آدرسی نداره، چون هنوز قرار نیست مقداری داشته باشه.

اما Value type ها نمیتونن Null باشند چون مقدار رو در خودشون نگه میدارند نه آدرس خونه حافظه رو. بنابراین قبل از استفاده از Value typeها باید حتما مقدار دهی شده باشند. اگه تکه کد زیر رو امتحان کنید خود Compiler بهتون خطا میده و بهتون میگه متغیری که تعریف کرده‌اید رو نمیتونید استفاده کنید تا زمانی که یک مقدار به آن نسبت بدید.

void someFunction()
{
    int i;

    Console.WriteLine(i);
}

 

یه چیز جالب دیگه اینکه، Value type هایی که به صورت یک Property در کلاس تعریف میشوند (نه به صورت محلی در تابع جاری)، زمانی که اونارو مقدار دهی نکنیم، مقدار پیش فرض رو به صورت اتوماتیک دریافت میکنند. مثلا متغیر از جنس integer مقدار 0 و یا boolean مقدار False رو به خودش اختصاص میده. مثال رو ببینید:

class myClass
{
    public int i;
}

myClass mcls = new myClass();

Console.WriteLine(mcls.i);

خروجی:

0

مقدار پیش فرض متغیرهای دیگه رو میتونید در سایت خود ماکروسافت ببینید.

یک نکته رو بگم که از C# 2.0 به بعد nullable typeها برای Value typeها معرفی شدند که میشه مقداری رو بهشون نسبت نداد، ولی خب این متغیرها قطعن دیگه از نوع مستقیم Value type نیستند.

 

نکاتی که باید یادمون بمونه:

1) Value typeها مقدار رو به همراه نام متغیر در خانه حافظه نگه داری میکنند در صورتی که Reference type ها فقط آدرس خونه ای از حافظه رو که مقدار اصلی در آنجاست را نگهداری میکنند.

2) تمامی داده های عددی و Structها Value type و class، string، array و delegate ها از نوع Reference type هستند.

3) Value type ها با به صورت پیش فرض با مقدار جابه‌جا میشن ولی Reference Typeها با آدرس.

4) در حافظه، Value type ها در Stack و Reference type ها با توجه به اندازه متغیر در Stack و Heap ذخیره میشوند.

 

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

قسمت اول | قسمت دوم


نظرات

برای نظر دادن کافیست وارد حساب کاربری خود شوید.