SajjadKhati
کاربر فعال <A href="http://forum.majidonline.com/f
چون value type ئه.
سلامی مجدد استاد .
خیلی ممنون .
الان یعنی در کد زیر :
C#:
((string style, int? width, int? height)view, (bool? isRunInStartup, bool? isEnableSilentMode,
(string driveName, bool? autoBackuping, int? autoBackupingValue)[] drivesBackupings, bool? isDisabledWindowsUpdate)model) settingTuples;
بجز آخرین لایه از پرانتز که مربوط به شیِ settingTuples که شی از از کلاس Tuple میشه هست ، بقیه ی جاهایی که (درونش) پرانتز داره ، یعنی مقادیری که به عنوان Item1 و Item2 و ... ، پرانتز دارن ، به عنوان شی ای از استراکچر هستن؟
یعنی قسمت زیر در کد بالا :
C#:
(string style, int? width, int? height)view
که مربوط به فیلدِ view (به عنوان Item1 ئه settingTuples) میشه ، یا قسمت زیر در کد بالا :
C#:
(bool? isRunInStartup, bool? isEnableSilentMode,
(string driveName, bool? autoBackuping, int? autoBackupingValue)[] drivesBackupings, bool? isDisabledWindowsUpdate)model
که مربوط به فیلد model (به عنوان Item2 ئه settingTuples) میشه (این هایی که کلا با علامت پرانتز نوع شون تعریف میشه بجز لایه ی آخری که settingTuples هست) ، اینها از نوع استراکچر هستن و از نوع class نیستن؟
چون مشخصه یا indexer یا متد ای که مقدار value type ای مثل struct و tuple رو بر می گردونه داره یک کپی از مقدار رو بر میگردونه، ارجاع به مقدار اصلی نیست. برای همین نمی توانید فیلدی رو در مقدار برگشتی تغییر بدهید.
صورت مساله رو ساده می کنم. شما یک struct ساده مثل A رو در نظر بگیرید :
می توانید مقدار Value رو (نوع داده Value مهم نیست) در A ای که یک value type ئه اینطوری تغییر بدهید؟C#:struct A { public object Value; }
نمی توانید. مشکل چیه؟ اون indexer در List به شما یک value type تحویل داده از نوع A که کپی مقدار اولین عضو لیست ئه.C#:var a = new List<A>(); a.Add(new A()); a[0].Value = null;
و indexer اصلا متوجه نخواهد شد که شما با فیلد Value در اون داده کپی شده چه می کنید. شما هر بلایی سر اون داده کپی شده بیاورید تاثیری روی a[0] نمیذاره چون ارجاع به مقدار نیست، یک کپی از داده است. می خواهید Value چیزی رو تغییر بدهید که ربطی به اون عضو در لیست نداره.
برای همین کامپایلر جلوی انجام این عمل بی فایده رو میگیره.
بله . خیلی ممنون استاد .
این رو قبلا هم گفتید .
اما اون موقع ، دقیق متوجه نمیشدم .
الان که بیشتر روی این قضیه (پروپرتی و استراکچر) تمرکز میکنم ، ببینین مطالب زیر ام در این رابطه درسته؟ :
در کد زیر :
کد:
public struct A
{
public object Value;
public int Digit;
public A(object val, int dig)
{
this.Value = val;
this.Digit = dig;
}
}
public class B
{
private A _myVariable;
public A MyVariable
{
get
{
return this._myVariable;
}
set
{
this._myVariable = value;
}
}
public void Method1()
{
this.MyVariable = new A(null, 10);
this.MyVariable.Digit = 20;
}
}
وقتی خطِ this.MyVariable = new A(null, 10) در متد Method1 اجرا میشه ، قسمت set ئه پروپرتیِ MyVariable اجرا میشه .
همونطور که قبلا گفته بودین ، پروپرتی ها ، دقیقا مثل متد میمونن . یعنی هر قسمت (همون accessor) شون که اجرا میشه ، دقیق مثل زمانی که یه متد اجرا میشه ، در حافظه ی استکِ نخ مورد نظرشون ، حافظه ای (ولو موقتی) براشون اختصاص داده میشه برای اجرا (حالا دقیقا جریان حافظه ی اشغالی توسط متدها و اینکه حجم شون چقدر میشه و بر اساس چه چیزی حجم شون کم یا زیاد میشه و اینها را نمیدونم و ممنون میشم در این باره هم توضیح بدین) .
در قسمت set ئه این پروپرتی هم مقدار جدیدی در متغییر _myVariable میریزه (حافظه ی این متغییر هم چون از نوع استراکچر هست ، استراکچرها هم که گفته بدوید به اندازه ی فیلدهاشون حافظه اشغال میکنن . پس این متغییر در ویندوز 32 بیتی ، 64 بیت و در ویندوز 64 بیتی ، 96 بیت حافظه اشغال میکنه دیگه؟)
قسمت set ئه پروپرتی اش (مثل همه ی پروپرتی ها) ، هم چیزی را برنمیگردونه ، پس بعد از اجرا ، اشاره گر به حافظه اش از دست میره و دیگه نیازی نمیشه.
============
وقتی خط this.MyVariable.Digit = 20 در متد Method1 اجرا بشه (که البته ارور میده) ، با فراخونیِ this.MyVariable ، قسمت get ئه پروپرتی تا اینجا اجرا میشه . مثل متدها ، برای اجرای قسمت get ، حافظه ای بهش اختصاص داده میشه .
در کد این قسمت :
C#:
get
{
return this._myVariable;
}
تا زمانی که میره مقدار متغییر _myVariable را میخونه ، تا اینجا ، مستقیما به خونه ی مورد نظر در این متغییر رجوع میکنه و تا اینجا ، کپی کردنی صورت نمیگیره (من قبلا احتمالا فکر میکردم حتی وقتی نامِ متغییری از نوع استراکچر را که نام ببریم ، مقدارش را کپی میکنه) .
اما وقتی که در کد بالا ، return میکنیم ، داریم مقدارِ مورد نظر را در حافظه ی اون متدمون که در استکِ نخ مون در نظر گرفته شده بود ، ذخیره میکنیم و چون اینجا ، صرفا رجوع کردن نیست بلکه در حافظه ی جدیدِ دیگه ای (حافظه ی متدمون) داریم میریزیم ، پس زمانی که در متدمون (شامل پروپرتی یا ایندکسر) ، نوعِ استراکچر را return و ذخیره میکنیم ، کل مقدار استراکچرِ مورد نظر را کپی میکنه توی حافظه اش .
پس در قسمت get ئه این پروپرتی ، مقدارِ کپی شده (در حافظه ی جدید) از متغییرِ _myVariable را داریم .
وقتی هم که خط this.MyVariable.Digit = 20 اجرا بشه ، مقدار فیلد Digit را در حافظه ی کپی شده (که مقدار بازگشتیِ قسمتِ get ئه پروپرتی مون بود که منظورمون این حافظه نیست) ، تغییر میده ، نه اینکه مقدار فیلد Digit در حافظه ی متغییر _myVariable را تغییر بده .
مثل تصویر زیر :
تصویر بالا ، برای زمانی هست که خطوط زیر ، در کدی که در بالا داده شد ، اجرا بشه :
C#:
this.MyVariable = new A(null, 10);
this.MyVariable.Digit = 20;
اما چون خط دوم ارور میده به دلایلی که گفته شد ، واسه ی همین از اجرای همچین کدی ، جلوگیری میشه .
------
من قبلا فکر میکردم که this._myVariable.Digit = 99 را هم که اجرا کنیم (دقت کنید که _myVariable ، نام فیلد هست ، نه نام پروپرتی) ، به صِرفِ اینکه نامِ فیلد هم برده بشه و زمانی که خونده هم میشه ، برابر با زمانی هست که return میشه و در حافظه ی دیگه ای ذخیره میشه که این ، درست نیست .
بلکه در این حالت (که نام فیلد فراخونی میشه) ، مستقیما به حافظه ی همون فیلد مراجعه میشه (اولین شکل در تصویر بالا که حافظه ی فیلد _myVariable هست) و دیگه کپی شدن (صِرفِ خوندنِ اون فیلد) ، معنا نداره .
درست گفتم استاد؟
تشکر