نوشته شده توسط
karimi72
دوستان عزیز
با یک مثال سوالم را شروع می کنم تا بهتر متوجه شوید :
اگر ما یک int را به صورت عادی (نه با ref , out ) به یک متد ارسال کنیم هر تغییری در متغییر دهیم در متغییر اصلی هیچ تغییری رخ نمی دهد .
حال سوالم این جاست که اگر یک شی را به صورت عادی بفرستیم اگر در متغییر های شی تغییر ایجاد کنیم این تغییرات ماندنی هستند یا مثل int که ابتدا مثال زدیم موقتی ؟
با سلام.
با توجه به مطالبی که در این تاپیک گفتم در حالت ارسال ByVal یا ارسال معمولی، مقدار متغیر های Value type در آدرسی که برای آرگومنت متود در نظر گرفته شده، کپی می شود، در نتیجه هر تغیر که در پارامتر ارسال شده اعمال شود مربوط به یک آدرس مجزا و یک متغیر مجزا بوده و هیچ تاثیری روی متغیر اصلی ندارد. اما متغیر های reference type (همونطور که در تاپیک بالا اشاره شد) صرفا یک اشاره گر هستند و آدرس مقادیر واقعی را در خود نگه می دارند.
اما وقتی که یک متغیر reference type به عنوان یک پارامتر و به صورت byval ارسال می شود چه اتفاقی می افتد؟
فرض می کنیم که b آرگومنت یک متودی است که ما aرا به آن ارسال می کنیم و a و b هر دو reference type هستند. در این حالت آدرسی که متغیر a به آن اشاره می کند در متغیر b کپی می شود. در واقع در این حالت a و b دو متغیر جداگانه هستند ولی هر دو اینها به یک خانه اشاره می کنند. درک این نکته خیلی مهم است که بدانیم در این حالت a و b دقیقا یکی نیستند ولی هر دو به یک مقدار اشاره می کنند. حالا مثلا اگر نوع این پارامتر یک لیست باشد و ما با استفاده از متود b.Add("x");
یک عضو به این لیست اضافه کنیم این تغییرات در متغیر a هم اعمال می شود چرا که همانطور که گفته شد هر دو به یک آدرس اشاره می کنند.
پس در این حالت تفاوت ارسال ByVal و ByRef در مورد متغیر های reference type چیست؟
همانطور که گفتیم در حالت byVal و در مثال قبلی a و b دقیقا یکی نبودند ولی به یک آدرس یکسان اشاره می کردند و اگر ما خصوصیتی از b را تغییر می دادیم این تغییر در a نیز اعمال می شد. حالا به نظر شما اگر ما در طول اجرای متود یک آدرس جدید در b قرار بدهیم چه اتفاقی می افتد؟
//b=new address...
b=new List<String>();
آیا آدرس متغیر a هم تغییر میکند؟
پاسخ منفی است. چون که در حالت ByVal a و b دو متغیر جدا بودند که به یک آدرس اشاره می کردند، پس اگر b به یک آدرس جدید اشاره کند a کماکان به آدرس قبلی اشاره می کند. در واقع در حالت ByVal و بعد از تغییر آدرس b کد زیر هیچ تغییری در a ایجاد نمی کند:
b.Add("y");
حالا c را آرگومنتی فرض کنید که ما d را به ازای آن به صورت ref ارسال می کنیم. در این حالت خود d دقیقا به c ارسال می شود و d و c یک متغیر هستند با دو نام. پس اگر c تغییر کند و یا حتی اگر به طور کلی یک شی جدید در c قرار بگیرد و آدرسش تغییر کند، تمام این تغیرات در d هم اتفاق خواهد افتاد. در واقع c در این حالت یک اشاره گر به اشاره گر یا همان چیزی است که در زبان C++ به آن **c
گفته می شد.
نتیجه گیری کلی:
1- در حالت ByVal تغییراتی که در آرگومنت داده می شود در متغیر اصلی هم اثر دارد:
void xxx (List<String> b)
{
b.Add("x");
}
List<String> a=new List<String>();
xxx(a);
MessageBox(a.Contains("x"));//true
3- در حالت ByVal اگر آرگومنت به یک شی دیگر اشاره کند ماهیت متغیر اصلی تغییر نمی کند:
void xxx2( List<String> b)
{
b=new List<String>();
b.Add("x");
}
///...
List<String> a=New List<String>();
xxx2(a);
MessageBox.Show(a.Contains("x").ToString());//false
3- در حالت ref هر تغییری که روی آرگومنت اعمال شود روی متغیر اصلی موثر است:
void xxx3(ref List<string> c)
{
c.Add("x");
}
//....
List<String> d=new List<String>();
xxx3(ref d);
MessageBox.Show(d.Contains("x").ToString());//true
4- درحالت ref هر تغییری که در ماهیت آرگومنت اتفاق بیفتد و حتی اگر آرگومنت به شی جدیدی اشاره کند تغییرات در متغیر اصلی موثر است:
void xxx4(ref List<String> c)
{
c=new List<String>();
c.Add("x");
}
//...
List<string> d=new LIst<String>();
MessageBox.Show(d.Contains("x").ToString());//true