گفتگو هایی در باب سی شارپ

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 رو در نظر بگیرید :
C#:
        struct A
        {
            public object Value;
        }
می توانید مقدار Value رو (نوع داده Value مهم نیست) در A ای که یک value type ئه اینطوری تغییر بدهید؟
C#:
            var a = new List<A>();
            a.Add(new A());
            a[0].Value = null;
نمی توانید. مشکل چیه؟ اون indexer در List به شما یک value type تحویل داده از نوع A که کپی مقدار اولین عضو لیست ئه.
و 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 را تغییر بده .
مثل تصویر زیر :


1.JPG


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

C#:
            this.MyVariable = new A(null, 10);
            this.MyVariable.Digit = 20;

اما چون خط دوم ارور میده به دلایلی که گفته شد ، واسه ی همین از اجرای همچین کدی ، جلوگیری میشه .

------

من قبلا فکر میکردم که this._myVariable.Digit = 99 را هم که اجرا کنیم (دقت کنید که _myVariable ، نام فیلد هست ، نه نام پروپرتی) ، به صِرفِ اینکه نامِ فیلد هم برده بشه و زمانی که خونده هم میشه ، برابر با زمانی هست که return میشه و در حافظه ی دیگه ای ذخیره میشه که این ، درست نیست .

بلکه در این حالت (که نام فیلد فراخونی میشه) ، مستقیما به حافظه ی همون فیلد مراجعه میشه (اولین شکل در تصویر بالا که حافظه ی فیلد _myVariable هست) و دیگه کپی شدن (صِرفِ خوندنِ اون فیلد) ، معنا نداره .

درست گفتم استاد؟
تشکر
 

the_king

مدیرکل انجمن
سلامی مجدد استاد .
خیلی ممنون .

الان یعنی در کد زیر :

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 و ... ، پرانتز دارن ، به عنوان شی ای از استراکچر هستن؟
کلاس tuple اینه :
که static ئه و طبعا شیء ای نمیتونه از کلاس static ایجاد بشه. و در ضمن کلاس ها reference type هستند، طبعا وقتی در توضیح tuple ها ذکر شده که value type هستند و توضیح دادم که دلیل اون خطا چیه، دیگه چه کلاسی؟
Tuple types are value types; tuple elements are public fields. That makes tuples mutable value types.
They are structures (value types) rather than classes (reference types).
یعنی قسمت زیر در کد بالا :

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 نیستن؟
کدوم کلاس؟ چرا اصلا باید کلاسی باشه یا نباشه؟ چرا میگید "الان یعنی"؟ در پست قبلی اصلا حرفی در مورد کلاس نزدم که موضوع کلاس بودن و نبودن باشه.
وقتی خطِ this.MyVariable = new A(null, 10) در متد Method1 اجرا میشه ، قسمت set ئه پروپرتیِ MyVariable اجرا میشه .

همونطور که قبلا گفته بودین ، پروپرتی ها ، دقیقا مثل متد میمونن . یعنی هر قسمت (همون accessor) شون که اجرا میشه ، دقیق مثل زمانی که یه متد اجرا میشه ، در حافظه ی استکِ نخ مورد نظرشون ، حافظه ای (ولو موقتی) براشون اختصاص داده میشه برای اجرا (حالا دقیقا جریان حافظه ی اشغالی توسط متدها و اینکه حجم شون چقدر میشه و بر اساس چه چیزی حجم شون کم یا زیاد میشه و اینها را نمیدونم و ممنون میشم در این باره هم توضیح بدین) .
در مورد ماهیت get و set در مشخصه ها توضیح تون درسته اما من هیچ صحبتی از حافظه و نخ ای نکردم.
استک نخ مورد نظرشون نمیدونم چیه، اگه در مورد زبان سی شارپ حرف می زنید، استک نخ و حافظه اشغالی متد ها و ... از این حرف ها رو بریزید دور، وجود خارجی نداره. اگر هم میخواهید کارکرد CLR یا CoreCLR نسخه فلان رو تحلیل کنید، باید بروید سراغ مهندسی معکوس اش که نمیدونم اصلا به چه دردی میخوره، از نظر من دانستن این چیزها برای برنامه نویس دات نت بیخود ئه. مثل اینه که بخواهید بدونید در بین تریلیون ها سلول داخل بدن تون هر کدوم الان داره چیکار میکنه.
در قسمت set ئه این پروپرتی هم مقدار جدیدی در متغییر _myVariable میریزه (حافظه ی این متغییر هم چون از نوع استراکچر هست ، استراکچرها هم که گفته بدوید به اندازه ی فیلدهاشون حافظه اشغال میکنن . پس این متغییر در ویندوز 32 بیتی ، 64 بیت و در ویندوز 64 بیتی ، 96 بیت حافظه اشغال میکنه دیگه؟)
نمیدونم حافظه ها ساختار رو بر مبنای چه معیاری محاسبه می کنید اما من نه همچین حرف هایی زدم و نه در مورد میزان حافظه مصرفی یک ساختار داده مدیریت شده توضیحی دادم. باز زدید تو جاده خاکی. حرف های از اینجور مواردی است :
مستندات خاصی نداره که این ساختار رو مشخص کنه، از اون مباحثی است که برای اینجور زبان ها که ساختار حافظه اشیاء پنهانه دانستنش فایده عملی نداره.
اشکال کار همینجا است، مطلبی نیست که بدردی بخوره. اگه کسی ندونه که اشاره گری هست یا ندونه که تغییر میکنه یا نمیکنه هیچ فرقی بحالش نمی کنه. علمی که منفعتی در اون نباشه دانستنش هم بیخوده.
صحبت همون فیل ئه و خرطومه است. ما در زبان #C اشاره گری برای این متغیر که میگید نداریم، وقتی چیزی وجود نداره و در زبان تعریف نشده، در مورد جابجا شدن چه چیزی بدونیم؟
فرض کنید حافظه قبلی باشه، یا فرض کنید حافظه جدید باشه. هر دو فرض به یک اندازه بی معنی هستند. الان این قضیه فیل و خرطومش چقدر علمی و مفید بود؟ این فرض شما هم همونقدر علمی و مفید میشه.
اسناد مایکروسافت در مورد پیاده سازی GC در CLR ئه، جزئی از ماشین مجازی که NET. رو اجرا می کنه. اصلا در مورد زبان #C و NET. نیست. اینکه GC با چه ساختار حافظه ای پیاده سازی بشه ربطی به زبان #C نداره، همین الان هم GC در Mono با ساختار های متفاوتی پیاده سازی شده که ربطی به مستندات مایکروسافت هم نداره. بحث زبان #C رو با این موارد نامربوط قاطی نکنید.

.
قسمت 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 را تغییر بده .
استک نخ دیگه چیه؟
بلکه در این حالت (که نام فیلد فراخونی میشه) ، مستقیما به حافظه ی همون فیلد مراجعه میشه (اولین شکل در تصویر بالا که حافظه ی فیلد _myVariable هست) و دیگه کپی شدن (صِرفِ خوندنِ اون فیلد) ، معنا نداره .
بله.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
کلاس tuple اینه :
که static ئه و طبعا شیء ای نمیتونه از کلاس static ایجاد بشه. و در ضمن کلاس ها reference type هستند، طبعا وقتی در توضیح tuple ها ذکر شده که value type هستند و توضیح دادم که دلیل اون خطا چیه، دیگه چه کلاسی؟



کدوم کلاس؟ چرا اصلا باید کلاسی باشه یا نباشه؟ چرا میگید "الان یعنی"؟ در پست قبلی اصلا حرفی در مورد کلاس نزدم که موضوع کلاس بودن و نبودن باشه.

سلامی مجدد
خیلی ممنون استاد .
پس کلاس Tuple ، هم استاتیک هست و هم value type هه . حالا چجوری هه که این ، برخلاف همه ی کلاس های دیگه که reference type هستن ، از یه کلاس ، value type ایجاد میکنن؟
استاد ، بعضی وقت ها ، اصطلاح mutable value type میگن ، این ، چه نوع value type هست؟

در مورد ماهیت get و set در مشخصه ها توضیح تون درسته اما من هیچ صحبتی از حافظه و نخ ای نکردم.
استک نخ مورد نظرشون نمیدونم چیه، اگه در مورد زبان سی شارپ حرف می زنید، استک نخ و حافظه اشغالی متد ها و ... از این حرف ها رو بریزید دور، وجود خارجی نداره. اگر هم میخواهید کارکرد CLR یا CoreCLR نسخه فلان رو تحلیل کنید، باید بروید سراغ مهندسی معکوس اش که نمیدونم اصلا به چه دردی میخوره، از نظر من دانستن این چیزها برای برنامه نویس دات نت بیخود ئه. مثل اینه که بخواهید بدونید در بین تریلیون ها سلول داخل بدن تون هر کدوم الان داره چیکار میکنه.

اشاره گر به حافظه چی؟ کدوم اشاره گر؟ کدوم حافظه؟ چی میگید؟ در کدوم پست من دیدید از این حرف ها بزنم؟

استک نخ دیگه چیه؟

استاد ، درباره ی حافظه ی پشته ی نخ ، خودتون توضیح داده بودید :


پست 1527 (پست در لینک بالا) در 3 نقل قولِ آخری تا توضیحات آخرتون .
همچنین در پست 1579 .
و همچنین صفحه 80 .

البته گفته بودین که مدیریت حافظه اش ربطی به gc نداره و به متد و اینها هم ربطی نداره که فراموش کرده بودم .

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

هر چند مقدارِ موقعیتِ اینکه اجرای نخ جاری در پردازنده ، کجا متوقف میشه ، در حافظه ی استکِ نخ ذخیره بشه (درسته دیگه؟) تا پردازنده در اجرای بعدی ، از اون موقعیت به بعد ، ادامه اش را اجرا کنه و ربطی به حافظه ی متد نداشته باشه .


نمیدونم حافظه ها ساختار رو بر مبنای چه معیاری محاسبه می کنید اما من نه همچین حرف هایی زدم و نه در مورد میزان حافظه مصرفی یک ساختار داده مدیریت شده توضیحی دادم. باز زدید تو جاده خاکی. حرف های از اینجور مواردی است :

استراکچرهای managed را نمیدونم ولی یادم میاد قبلا گفته بودید که اندازه ی استراکچر ، به اندازه ی فیلدهای اون هست (نمیدونم منظورتون استراکچرِ managed بود یا unmanaged) .
 

the_king

مدیرکل انجمن
سلامی مجدد
خیلی ممنون استاد .
پس کلاس Tuple ، هم استاتیک هست و هم value type هه . حالا چجوری هه که این ، برخلاف همه ی کلاس های دیگه که reference type هستن ، از یه کلاس ، value type ایجاد میکنن؟
کلاس Tuple استاتیک ئه و value type هم طبعا نیست، شی ای هم که ازش ساخته نمیشه که بخواهید value type باشه. اشیاء Tuple که از روی کلاس static ایجاد نمیشن. عرض کردم، ساختار داده اشیاء Tuple از نوع value type ئه. برگردید به پست قبلی من و اون لینک سوم که عنوانش ValueTuple Struct (System) بود رو ببینید. حتی می توانید یک شیء Tuple بسازید و در اجرا مقدار ()GetType().ToString. اش رو بررسی کنید.
همونطور که در توضیحات اون کلاس static نوشته بود (لینک اول)، متد های static ای که برای ایجاد Tuple استفاده می شوند رو ارائه می کنه، نه نوع داده خود اشیاء Tuple.
استاد ، بعضی وقت ها ، اصطلاح mutable value type میگن ، این ، چه نوع value type هست؟
ویژگی های متفاوتی هستند، یعنی mutable یا immutable بودن رو مستقل از اون value type یا reference type بودن در نظر بگیرید، مفاهیم مستقل ای هستند. فرض کنید که مقدار یک متغیری x ئه که در جایی از حافظه قرار داره، حالا اگر مقدار اون متغیر رو به مقدار متفاوت y تغییر بدهید دو حالت پیش میاد، یا y روی همون حافظه ای که x بود ذخیره میشه (mutable) یا در موقعیت حافظه متفاوتی ذخیره میشه (immutable)
یعنی اگر نوع داده ای mutable باشه، مقدار داخل اون حافظه قابل تغییر ئه، میتونه ویرایش بشه. ولی در immutable ها مقدار قبلی دیگه قابل ویرایش نیست، مقدار جدید در حافظه متفاوتی قرار میگیره.
فرضا برای نوع داده string نوشته شده :
String objects are immutable: they cannot be changed after they have been created.
یعنی وقتی مقدار یک string رو تغییر می دهید مقدار در حافظه قبلی ویرایش نمیشه، اون همونطور دست نخورده می مونه. بلکه به آدرس حافظه متفاوتی اشاره می کنه که مقدار جدید در اون قرار داره.
طبعا ;int i = 4 ویژگی mutable داره، چون وقتی مقدار 4 رو به 5 تغییر می دهید مقدار در همون آدرس حافظه تغییر می کنه.
استاد ، درباره ی حافظه ی پشته ی نخ ، خودتون توضیح داده بودید :


پست 1527 (پست در لینک بالا) در 3 نقل قولِ آخری تا توضیحات آخرتون .
همچنین در پست 1579 .
و همچنین صفحه 80 .
اونجا گفتم هر نخ ای در پردازنده فیزیکی یا مجازی ای یک پشته داره و مدیریت اش هم ارتباطی با GC نداره. اتفاقا همونجا هم گفتم که حرف هایی می زنید که ارتباطی با GC و #C و حافظه مدیریت شده نداره. "داریم مقدارِ مورد نظر را در حافظه ی اون متدمون که در استکِ نخ مون در نظر گرفته شده بود ، ذخیره میکنیم" از کدوم صحبت من اومده؟ استک نخ مون کدوم نخ ئه؟ این حرف ها از کجا میاد؟
البته گفته بودین که مدیریت حافظه اش ربطی به gc نداره و به متد و اینها هم ربطی نداره که فراموش کرده بودم .
فقط جستجو کنید و ببینید چند بار گفتم اون بحث کذایی stack در #C که نمیدونم از کجا رفته تو ذهن تون رو ول کنید و نکردید.
یک کلاس Stack و <Stack <T هست که در حافظه مدیریت شده NET. براتون پشته میسازه و تمام، فقط همینه.
هر چند در مورد حافظه ی متدها چندان تحقیق نکردم ولی به نظر میرسه که زمانی که هر متد اجرا میشه ، باید حافظه ای براش در نظر گرفته بشه که هم مقادیر پارامترهای ورودی اش را جایی ذخیره کنه و هم اینکه اگه خروجی ای داره ، توی اون حافظه ذخیره بشه و متغییرهای محلی اش هم همینطور .
یکی دیگه اش هم اینه که اون متد وقتی بصورت بازگشتی اجرا بشه ، مقادیر متفاوت ، مربوط به متد مورد نظر ، ذخیره بشه .
درست میگم استاد؟ یعنی هر متد که اجرا میشه ، حداقل برای ذخیره ی این اطلاعاتش ، نیاز به حافظه و بافر داره .
اگه آره ، اطلاعات بیشتری اگه در این رابطه دارین ، ممنون میشم بگین .
بحث مفصلی هست به عنوان Calling Convention که طراح هر معماری مشخص می کنه که فرضا موقع فراخوانی یک متد که فلان تعداد پارامتر و مقدار بازگشتی داره به چه روشی مقادیر پارامتر ها یا مقدار بازگشتی مشخص بشوند. مشخصات پردازنده، کامپایلر و معماری زبان برنامه نویسی و ... باعث شده روش های مختلفی بوجود بیاد. انواع مختلف روش ها برایش هست، یکی دو تا نیست. هر کدوم هم برای یکسری معماری خاص معنی داره. فرضا در پردازنده ای که رجیستر های فلان رو نداره، روشی که از اون رجیستر ها استفاده می کنه کاربردی نداره. برخی زبان های سطح پایین مثل ++C بیش از یک روش رو می شناسند و بکار می برند. اما این یک موضوع سطح پایین ئه (low-level) و در کل مربوط به معماری ماشین (مجازی یا فیزیکی) و دانستن اش برای زبان های سطح بالا مثل #C کاربردی نداره.
هر چند مقدارِ موقعیتِ اینکه اجرای نخ جاری در پردازنده ، کجا متوقف میشه ، در حافظه ی استکِ نخ ذخیره بشه (درسته دیگه؟) تا پردازنده در اجرای بعدی ، از اون موقعیت به بعد ، ادامه اش را اجرا کنه و ربطی به حافظه ی متد نداشته باشه .
کدوم حافظه متد؟ این حرف ها از کجا میاد؟ نه. قبلا چند بار اشاره کردم، متد شما جزئی از مفاهیم زبان سطح بالای #C شما است، پردازنده این چیزها رو درک نمی کنه. پردازنده فقط فرمان هایی که به زبان ماشین بهش دادن پشت سر هم اجرا می کنه، خودش هم تصمیم نمی گیره که کجا رو اجرا کنه یا چه مقداری رو از کجا برداره و باهاش چیکار کنه.
استراکچرهای managed را نمیدونم ولی یادم میاد قبلا گفته بودید که اندازه ی استراکچر ، به اندازه ی فیلدهای اون هست (نمیدونم منظورتون استراکچرِ managed بود یا unmanaged) .
من یادمه، میخواستید بخاطر اتوپلی با DLL با حافظه مدیریت نشده ارتباط برقرار کنید و اندازه حافظه لازم برای ذخیره سازی ساختار های ++C/C در حافظه مدیریت نشده که فرضا در API ویندوز توصیف شده بود رو بر اساس تعداد فیلد ها و نوع داده فیلد های ساختار محاسبه می کردید.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلامی مجدد استاد .
خیلی ممنون از توضیح مفصل تون .

استاد ، از اونجایی که لپتاپ گرفتم و یه پروژه را فرضا یه مقداری اش را روی سیستم pc انجام میدم ، و بعد (وقتی که بعد از اون ، با لپتاپ کار میکنم) میخوام ادامه ی اون را در لپتاپ انجام بدم ، و باز مجددا ادامه اش را در سیستم pc انجام بدم و به همین طور این قضیه ادامه داره (یعنی ادامه ی آخرین اصلاحاتی که روی پروژه در لپتاپ یا pc انجام شد را در یکی دیگه انجام بدم و تکمیل کنم) .

بهترین راهکار برای این قضیه ، چیه؟
فرضا اینه که از قضیه و راهکارهایی که github در ویژال استودیو ارائه داده استفاده کنم (البته هنوز از github استفاده نکردم . اما اگه اشتباه نکنم ، برای همین قضیه طراحی شده . درست میگم؟) .

یا اینکه توی فلش بریزم و به لپتاپ و pc انتقال بدم؟
یا راهکار دیگه ای هم وجود داره؟

تشکر استاد
 

the_king

مدیرکل انجمن
سلامی مجدد استاد .
خیلی ممنون از توضیح مفصل تون .

استاد ، از اونجایی که لپتاپ گرفتم و یه پروژه را فرضا یه مقداری اش را روی سیستم pc انجام میدم ، و بعد (وقتی که بعد از اون ، با لپتاپ کار میکنم) میخوام ادامه ی اون را در لپتاپ انجام بدم ، و باز مجددا ادامه اش را در سیستم pc انجام بدم و به همین طور این قضیه ادامه داره (یعنی ادامه ی آخرین اصلاحاتی که روی پروژه در لپتاپ یا pc انجام شد را در یکی دیگه انجام بدم و تکمیل کنم) .

بهترین راهکار برای این قضیه ، چیه؟
بهترین راهکار برای هر کسی متفاوته، بهترینی وجود نداره که من برای شما مشخص کنم. می توانید پروژه رو روی یک فلش درایو یا هارد دیسک قابل حمل جابجا کنید یا از GitHub یا Azure DevOps و ... استفاده کنید. در هر صورت باید بصورت دوره ای در جای دیگری نسخه بروز شده پروژه رو نگهدارید.

Top 10 GitHub Alternatives That You Can Consider
فرضا اینه که از قضیه و راهکارهایی که github در ویژال استودیو ارائه داده استفاده کنم (البته هنوز از github استفاده نکردم . اما اگه اشتباه نکنم ، برای همین قضیه طراحی شده . درست میگم؟) .
بله.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلامی مجدد
استاد واقعا ببخشید که اینو میگم . چون میدونم یه بار دیگه هم قبلا گفته بودم .

قبلا (چند سال پیش) یه پروژه ای بود که برای قضیه ی رقص نور برای mp3 ها داده بودین . میخواستم که نمودار تغییرات صدا را بگیرم تا مثل پلیرهایی که موقع پخش آهنگ ، رسم میکنن ، این کار را کنم (که شما یه پروژه ای داده بودین) .

هر چی درباره ی عبارت رقص نور در این تاپیک و کلا انجمن مجید آنلاین میگردم ، پیدا نمیکنم (همچنین نام اون آهنگ که Khabari Hast (128) بود) .
پست 817 و چند تایی را پیدا کردم اما اون چیزی نبود که مد نظرم بود و همچنین اون پروژه تون هم نبود .
توی سیستم هم اگه اشتباه نکنم ، ذخیره کرده بودم اما نمیدونم نامش چی بود که هر چی شبیه این نام ها را توی پروژه های سیستمم میبینم ، نیست .

بحث هم درباره ی این بود که من تایمر هم که گذاشته بودم ، چون اندکی دقیق نبود ، همین باعث میشد که به مرور زمان ، با روند آهنگ که هر چی جلوتر میرفت ، هماهنگی اش کمتر بشه که شما به یه ترتیبی که الان یادم نمیاد چی بود ، توی اون پروژه ، این قضیه را حل کرده بودین .


ببخشید که دوباره میپرسم ولی اگه زحمتی نیست میتونین دوباره لینک اون مطالب را که نمیدونم توی این تاپیک بود یا نه ، بدین؟

من آخر نحوه ی خوب جستجو کردن را دقیق یاد نگرفتم که فرضا در این مواقع ، چه چیزی را باید جستجو کنم (چون عبارات که یادم نمیاد دقیق چی گفتیم اما یادم میاد که واژه های "رقص نور" یا نام اون فایل صوتی را بکار برده بودم) .
تشکر :rose:
 
آخرین ویرایش:

the_king

مدیرکل انجمن
سلامی مجدد
استاد واقعا ببخشید که اینو میگم . چون میدونم یه بار دیگه هم قبلا گفته بودم .

قبلا (چند سال پیش) یه پروژه ای بود که برای قضیه ی رقص نور برای mp3 ها داده بودین . میخواستم که نمودار تغییرات صدا را بگیرم تا مثل پلیرهایی که موقع پخش آهنگ ، رسم میکنن ، این کار را کنم (که شما یه پروژه ای داده بودین) .

هر چی درباره ی عبارت رقص نور در این تاپیک و کلا انجمن مجید آنلاین میگردم ، پیدا نمیکنم (همچنین نام اون آهنگ که Khabari Hast (128) بود) .
پست 817 و چند تایی را پیدا کردم اما اون چیزی نبود که مد نظرم بود و همچنین اون پروژه تون هم نبود .
توی سیستم هم اگه اشتباه نکنم ، ذخیره کرده بودم اما نمیدونم نامش چی بود که هر چی شبیه این نام ها را توی پروژه های سیستمم میبینم ، نیست .
من به اسم اسپکتروم (Spectrum Effect) میشناسم. مثلا پست #816 و #817
خودم یک پروژه SoundWave و SoundWave.exe دارم اما برای فایل های wav بوده نه mp3
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
خیلی ممنون استاد . :rose:

استاد ، مدت ها قبل ، یکی ، در اون انجمن درباره ی مطلبی پرسید که نهایتا درباره ی async await ها براش توضیح دادم .
این توضیحات را از توضیحاتی که قبلا در اینجا درباره ی این مطلب بهم داده بودید و همچنین از منابع مایکروسافت و بقیه ی منابع گرفتم .

به نظرم اومد چون حداقل در اون انجمن ، گاها پیش میاد که وقتی از مطلبی ، چند سال بگذره ، ممکنه کدهایی که نوشته بودیم ، یه تگ هایی بهش اضافه بشه و خوندنش خیلی سخت بشه (نمیدونم چرا این طوریه و گاها پیش اومد که در اونجا ، به کدم ، تگ های color و اینها اضافه میشد) و همچنین برای یادآوری خودم ، اون مطلب را در این پست هم بذارم (البته اگه توضیحاتم اشکال داره ، ممنون میشم بگیرید) .
تشکر استاد

====================

توضیحات درباره ی Asysnc Await :

کد زیر :

C#:
private string GetHttpData()
{
    try
    {
        //"google.com"
        // "https://www.youtube.com/channel/UC81uuy-UsdB5u-ZHiYp2XSg/videos"
        // "https://www.shahrsakhtafzar.com/fa/"
 
 
        HttpWebRequest web = (HttpWebRequest)WebRequest.Create("https://www.youtube.com/channel/UC81uuy-UsdB5u-ZHiYp2XSg/videos");
        web.Method = "Get";
        HttpWebResponse res = (HttpWebResponse)web.GetResponse();
        string source = new StreamReader(res.GetResponseStream()).ReadToEnd();
        return source;
    }
    catch
    {
        return null;
    }
}
 
 
private async void Button_Click(object sender, RoutedEventArgs e)
{
    Task<string> httpDataTask = new Task<string>(new Func<string>(this.GetHttpData));
    // با اجرای این خط ، متدِ GetHttpData ، در نخ جدیدی اجرا میشه .
    httpDataTask.Start();
    // با اجرای await ، یعنی با اجرای این خط ، ادامه ی اجرای متد جاری ، یعنی ادامه ی اجرای رویدادِ Button_Click که توش هستیم ، معلق میشه و -
    // و کنترل اجرای کدها ، به متدها یا رویدادهای دیگه ای که در صفِ نخِ جاری مون هستن میپردازه .
 
 
 
 
 
    // همچنین await ، منتظر میمونه تا متد GetHttpData که توسط متد Task.Start یا توسط Task.Run که در خط بالا در نخ جدیدی اجرا شد ، خاتمه پیدا کنه تا -
    // تا بعد از اتمامش ، اولا ، هم در صورتی که مثل این مثال ، اگه متدمون که GetHttpData هست ، مقداری را برگردوند ، نتیجه ی اون ، یعنی مقدار بازگشتی اش را که رشته هست ، برگردونه و -
    // و دوما ، اینکه بعد از اتمامش ، ادامه ی کد در این متد را اجرا کنه . یعنی خط های پایین اش را اجرا کنه که در همون نخ که در اینجا نخ اصلی هست ، اجرا کنه .
    string httpDataString = await httpDataTask;
 
 
    if (httpDataString != null)
        HttpReqTextBlock.Text = httpDataString;
    else
        MessageBox.Show("خطایی در ارتباط رخ داده است", "هشدار");
}

درون متدی که از کلمه ی کلیدیِ await استفاده میکنید ، باید برای اون متد ، از کلمه ی کلیدیِ async استفاده کنید .
در واقع ، کلمه ی کلیدیِ async ، یه دلیگیت یا متد لازم داره که درونش از await استفاده بشه .

خود await ، به یک شیِ Task یا Tast<T> ای که قبلا اجرا شده باشه ، نیاز داره . که این شی را کنار این کلمه ی کلیدیِِ await مینویسیم .
یعنی قبل از استفاده از کلمه ی کلیدیِ await ، این شیِ Task یا Tast<T> ، باید اجرا شده باشه . حالا با متد Task.Start یا Task.Run یا Task.WhenAll قبلا باید اجرا شده باشه یا اینکه متدی را که فراخونی میکنیم و مقدار بازگشتیِ از نوع Task یا Task<T> داره ، داخلِ خودِ متد اون شیِ Task یا Task<T< را فراخونی کرده باشن .


در مثال بالا ، در متد Button_Click ، اول شیِ httpDataTask (از نوع Task<string>) ساخته شد و چون قبل از اجرای await باید اجرا بشه ، در خط بعدش با متد Task.Start ، اجرا شد . با اجرا شدنِ شیِ Task<string> ، متد GetHttpData درون نخ جدیدی اجرا میشه (نخ از نوع Background هست) .
چون متد GetHttpData ، در نخ جدید اجرا میشه ، پس همزمان ادامه ی نخ جاری مون اجرا میشه که در اینجا همون ادامه ی کد در متدِ Button_Click در نخ جاری (اصلی) مون هست .

بنابراین خط بعدش که کلمه ی کلیدی await هست ، اجرا میشه .
با اجرا شدن کلمه ی کلیدی await ، کارهای زیر انجام میشه :

- اول اینکه اجرای متدِ جاری (در اینجا متد Button_Click) ، به حالت تعلیق درمیاد . یعنی موقتا ، کدهایی که در این متد (Button_Click) در پایینِ خطِ await هستن ، اجرا نمیشه . بنابراین کنترلِ اجرای کد ، به متدها و رویدادهای دیگه ای که در اون اون نخ (نخ اصلی در اینجا) در صف انتظار هستن ، میرسه و اونها اجرا میشن و همین باعث میشه که برنامه از هنگیِ ، خارج بشه .

دقت کنید که اشتباه نکنید که چون شیِ Task را کنار await مینویسیم ، پس await باعث میشه که متدِ مورد نظر در اون Task (در اینجا متد GetHttpData) ، متوقف بشه ها . این طور نیست . بلکه ادامه ی اجرای کد در همون متدی که await توش نوشته شده را به حالت تعلیق درمیاره .

یعنی await (تا اینجا) ، کاری شبیه به کلمه ی کلیدی yield (در متد) انجام میده .

- دوم اینکه حالا await ، تا چه زمانی ادامه ی اجرای کد در متد را به حالت تعلیق در میاره؟
قبلا که شیِ Task مون اجرا شده بود (با متد Task.Start در این مثال) . که متد GetHttpData را در نخ جدیدی اجرا کرده بود .
تا زمانی که این Task مون خاتمه پیدا کنه . یعنی بعد از اتمام اجرای متد GetHttpData ، ادامه ی await در متد Button_Click اجرا میشه .

- سوم اینکه اگه شیِ Task مون ، نتیجه ای داشته باشه ، await ، نتیجه ی اون را میتونه برگردونه . یعنی مقدار پروپرتیِ Task.Result را که در اینجا همون مقدار بازگشتیِ متدِ GetHttpData مون هست را میتونه برگردونه .


* کلمه های کلیدیِ await و yield ، از لحاظِ به حالتِ تعلیق درآوردنِ متدی که توشون از این کلمات کلیدی استفاده شده ، شبیه هم هستن (یعنی هر دوشون ، متد را معلق میکنن و ادامه ی کدهای اون متد به صورت موقت ، اجرا نمیشه) منتها در زمان اجرای ادامه ی کدهای متد ، فرق میکنن .
که await ، زمانی که اجرای شیِ Task یا Task<T> ای که براش استفاده شده ، به اتمام برسه ، ادامه ی کدهای متدش اجرا میشه اما کلمه ی کلیدیِ yield ، باید خودمون (معمولا از جایی بیرون از متدی که در اون استفاده شد) اون متدی که yield در اون استفاده شده را مجددا فراخونی کنیم تا ادامه ی کدهاش اجرا بشه .

============

البته دقت کنید که این مثال ، بر پایه ی کد قبلی تون بود . یعنی بر پایه ی شیِ HttpWebRequest که همونطور در منبع اش اومده ، این کلاس قدیمی هست و بجاش از شیِ HttpClient توصیه شده استفاده کنید که در مثال پست 6 ، خودتون این کار را کردید .
که در این نوع اشیاء (مثل شیِ HttpClient) ، خودشون متدی که با نامِ Async ختم میشن و درون شون شیِ Task یا Task<T> ئه اجرا شده هست را دارن .


اطلاعات بیشتر :

Asynchronous programming - C#‎‎‎‎‎‎‎‎‎ | Microsoft Docs

و

Async and Await In C#‎‎‎‎‎‎‎‎‎ (c-sharpcorner.com)
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلام استاد .
استاد ، کلمه ی کلیدی volatile ، انگار برای این هست که فیلدی که این کلمه ی کلیدی براش بکار میره ، توسط نخ هایی که همزمان اجرا میشن ، بتونن ویرایش بشن (البته در پردازنده های چند هسته ای ، هیچ تضمینی برای خوندن یا نوشتن از آخرین مقداری که آخرین نخ در اون انجام داد ، وجود نداره که در قضیه ی نخ های همزمان ، بدیهی هست) :


درست متوجه شدم؟

اما سئوال اینه که اصلا چه نیازی به این کلمه ی کلیدی هست؟
چون در قضیه ی نخ ها ، حتی بدون استفاده از این کلمه ی کلیدی ، همین روال وجود داره دیگه . درست میگم؟

یعنی در قضیه ی نخ ها ، بصورت عادی ، چندین نخ ای که همزمان اجرا میشن (بدون استفاده از کلمه ی کلیدی lock یا async await و چند نمونه ی دیگه از این موارد) ، همه ی فیلدها ، در دسترس همه ی نخ ها بصورت مشترک قرار دارن .

اگه درست میگم ، پس چه احتیاجی به استفاده از این کلمه ی کلیدی هست؟
اگه درست نمیگم ، پس تفاوت شون (تفاوت حالت عادی و حالتی که از این کلمه ی کلیدی استفاده میشه) در چیه؟

تشکر استاد .
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلامی مجدد استاد .

اینو بعدا برای خودم میگم (اگه اشکال داره ، بی زحمت تصحیح کنید) :

- اینکه در طراحی الگوریتم ، یه اصل بنام "برنامه نویسی پویا" هست که میگه اگه قبلا یه کد یا متدی را اجرا کردین و مقدارش را بدست آوردین ، بعدا نباید همون (همون کد یا متد) را مجددا اجرا کنید ؛ بلکه باید مقدارش را ذخیره و از مقدار ذخیره شده اش استفاده کنید (در آخر ، الگوریتم فیبوناچی مربوط به این قضیه را میذارم) .

- یه موضوع دیگه اینکه توی متدهای بازگشتی هم هر وقت یه مسئله ای را به میزان کوچیکتر (یا گاها بزرگتر) بتونیم تقسیم کنیم ، از متدهای بازگشتی میتونیم استفاده کنیم .

- یا در واقع ، اغلب اوقات ، بجای استفاده از حلقه ، میتونیم از متدهای بازگشتی استفاده کنیم (مخصوصا در زمان هایی که تعدادِ for هایی که برای حلقه میذاریم (یا تعداد while ها) ، مشخص نباشن ؛ مثل زمانی که میخوایم یه درخت را پیمایش کنیم که مجبوریم برای هر سطح از درخت ، یه حلقه ی for به تعداد فرزندان اون گره بنویسیم که نمیدونیم تعدادشون چند تاست . بنابراین از متدهای بازگشتی برای پیمایش درخت استفاده میکنیم) .

- یه موضوع دیگه اینکه اطلاعات درخت پیمایش شده را یا بصورت آرایه های دندونه ای ذخیره کنیم (به نظرم این روش راحت تر هه) یا اینکه بصورت ساختار پیوندی (شکل زیر) میتونیم ذخیره کنیم (که این هم انگار بد نیست اما هر چند پیاده سازیِ این ساختار ، احتمالا سخت تر و سرعت پیمایش اش هم احتمالا کندتر باشه) :


1 Sakhtare Peyvandi Baraye Peymayesh Derakht.JPG

که در شکل بالا ، هر گِرِه (node) ، یک ساختار (کلاس یا استراکچر ؛ معمولا کلاس در نظر میگیرم) هست که شامل 3 فیلد (یا پروپرتی و ...) هست که اولین فیلدش ، مقدار اون گره ، و دومین فیلدش ، اشاره گری به گروه پدر ، و سومین فیلدش ، اشاره گری به آرایه ای از اشاره گر هست که هر کدوم از اعضای این آرایه ، اشاره گری به شیِ گِرِه (node) از فرزندانش هست .

==========

اما پیاده سازی متد بازگشتی تابع فیبوناچی به روش برنامه نویسی پویا (که اطلاعات را ذخیره میکنه) را بصورت زیر نوشتم :

C#:
        private void Fibonuchi1Button_OnClick(object sender, RoutedEventArgs e)
        {
            int fibonacci = this.FibonucciTextBox.Text.ToInt32(-1);
            if (fibonacci < 0)
                MessageBox.Show("عدد مثبت وارد کنید .");

            this.fibonaccies = new long[fibonacci + 1];
            fibonaccies[0] = 0;

            long resault = this.RecursiveFibonacci(fibonacci);
            MessageBox.Show(resault.ToString());
        }


        private long[] fibonaccies;

        private long RecursiveFibonacci(int fibonacci)
        {
            if (fibonacci == 1 | fibonacci == 0)
                return fibonacci;
            long oneLessFibonacci = this.RecursiveFibonacci(fibonacci - 1);

            fibonaccies[fibonacci - 1] = oneLessFibonacci;
            return oneLessFibonacci + fibonaccies[fibonacci - 2];
        }


در کد بالا ، اطلاعات مقادیر فیبوناچی را که بدست آورد را ذخیره میکنه و عمل جمع ، توسط آرایه ی سراسری که مقدارش بدست اومد ، انجام میشه .

یعنی برخلاف الگوریتم زیر :

C#:
        private long fib_2(long n)
        {
            if (n <= 1)
                return n;
            return fib_2(n - 1) + fib_2(n - 2);
        }

که بصورت تصاعدی ، تعداد فراخونی های متد بازگشتی اش بخاطر اینکه 2 بار متد بازگشتی را فراخونی کرد ، زیاد میشه ، الگوریتم اولی ، بخاطر یک بار فراخونی کردنش ، خیلی سریعتر هست .

در الگوریتم اول (و کلا متد بازگشتی) ، چون کدهایی که بعد از فراخونیِ متدِ RecursiveFibonacci نوشته شد ، زمانی اجرا میشه که آخرین متد را اجرا کرده باشه (یعنی زمانی که فیبوناچی 1 را اجرا کرده باشه) ، پس کدهای بعد از این متد ، زمانی اجرا میشن که جوابِ مقدار فیبوناچیِ های قبلی اش را دارن و اونها را در آرایه ی سراسری ، ذخیره شون کردیم .
و با فیبوناچیِ 2 تا کمتر در آرایه ، جمع میکنیم .

البته در الگوریتم اول ، فقط باید در ابتدا ، مقدار فیبوناچیِ عددِ 0 (که مقدارش 0 هست) را در آرایه ، ذخیره کنیم (هر ایندکس از آرایه هم مقدار مربوط به فیبوناچی خودش را ذخیره میکنه) .

تشکر استاد :rose:
 
آخرین ویرایش:

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلام استاد
درباره اصل وارونگی وابستگی (Dependency Inversion Principle) ، سئوال داشتم .


طبق این اصل انگار میگه که :
- ماژول های (فرضا کلاس های) سطح بالا ، نباید (بصورت مستقیم) به ماژول ها (و کلاس های) سطح پایین ، وابسته باشن .
هر دوی این ماژول ها باید به انتزاع (معمولا Interface ها یا شاید هم کلاس های abstract) وابسته باشن .

- دومی هم که دقیق متوجه نشدم میگه که انتزاع نباید به جزئیات وابسته باشه ؛ بلکه باید برعکس باشه .

-----------

جوری که متوجه شدم ، انگار کلاس های سطح پایین ، به کلاس هایی میگن که شی ای از کلاسِ مورد نظر را در خودش نداره .
کلاس های سطح بالا هم کلاس هایی هستن که شی ای از کلاسِ موردِ نظرِ دیگه ای ، درون اونها ساخته میشه .

فرضا در عکس زیر ، کلاس C ، نسبت به کلاس B ، کلاس سطح بالا محسوب میشه چون در کلاس C ، شی ای از کلاس B ساخته میشه .
همچنین خودِ کلاسِ B ، نسبت به کلاس A ، سطح بالا محسوب میشه چون درون کلاس B ، از کلاس A ، شی ساخته میشه .


SOLID.jpg


- دقت کنید که شکل بالا ، رابطه ی ارث بری ندارن ها . رابطه شون این طوره که درون کلاس بالاتر ، شی ای از کلاس پایین ترشون ایجاد میشه .

حالا این اصل میگه که مستقیما در کلاس سطح بالا ، از کلاس سطح پایین ، شی نسازیم . در واقع نوع داده ای مون را از نوع کلاس سطح پایین تر نگیریم (یعنی شکل بالا ، اصل DIP یا وارونگی وابستگی را نقض میکنه) و بجای اینکار ، کلاس سطح پایین را از یه انتزاع (مثلا اینترفیس ها) پیاده سازی کنیم و موقع ایجاد شی در کلاس سطح بالاتر ، نوع داده ای را از نوع اون اینترفیس بگیریم .

اول از اینکه مطالبی که در بالا گفتم را درست متوجه شدم؟
دوم از اینکه ، اون بخش دوم را که گفتم متوجه نشدم ، کسی توضیحی میده؟

==========

اما مهمترین سئوال ام (که هدف از این تاپیک بود) اینه که اگه در یه پروژه (مخصوصا پروژه های بزرگ) ، این طور بخوایم پیش بریم ، اصلا یه چیزِ قاتی پاتی و گیج کننده ای میشه که .
درست میگم؟

فرضا کنید در شکل بالا که کلاس A ، نسبت به کلاس B ، کلاسی سطح پایین محسوب میشه ، بخاطر این اصل ، کلاس A ، اینترفیسی را پیاده سازی کنه و درون کلاس B هم نوع داده ای را از اون اینترفیس در نظر گرفته بشه اما شی اش از نوع کلاس A داده بشه .

حالا خود کلاسِ B ، نسبت به کلاس C ، کلاسی سطح پایین محسوب میشه . پس این هم باید یه اینترفیس دیگه ای پیاده سازی کنه و درون کلاس C ، نوع داده ای اش ، از نوع اون اینترفیس و شی اش از کلاس B ساخته بشه .

--------

به همین ترتیب ، حتی لایه های دیگه و ارتباط دو لایه هم با هم ، باید این طور بشه دیگه؟ درسته؟

به این ترتیب ، اصلا نباید در هیچ پروژه ای ، نوع داده ای ، از نوعِ کلاسی وجود داشته باشه (مگر اینکه اون نوع ، کلاسِ abstract یا اینترفیس بوده باشه) .
مخصوصا اگه الگوی طراحیِ تزریق وابستگی هم در کنارش در نظر بگیریم .

اما به وفور میبینیم که خود مایکروسافت هم از نوع کلاس ، به عنوان داده هاش استفاده میکنه (البته از اینترفیس ها هم استفاده میکنه) .

سئوال اینجاست اگه روند ، بصورتی که گفتم ، نیست (که احتمالا نیست) ، یعنی همه ی انواع داده ای در کلاس سطح بالاتر ، از نوع انتزاع و اینترفیس نیستند ، پس این اصل وارونگی وابستگی ، باید در شرایطی محدود اجرا بشه (وگرنه همه ی انواع داده ، بجز در بالاترین سطح کلاس ، باید از نوع انتزاع میبودند که این طور نیست) .

اگه این طوره ، این شرایط ، چه شرایطی هستند؟
یعنی در صورتِ وقوعِ چه شرایطی ، اصل وارونگی وابستگی را در پروژه مون اجرا میکنیم؟

تشکر استاد :rose:
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلام استاد

در الگوی Repository ، چند تا سئوال دارم :






اولا ، این الگو ، برای جدا کردن لایه ی منطق تجاری (یا Model یا Domain) با لایه ی داده (یا Data) هست دیگه . درسته؟
یعنی این لایه ، چیزی شبیه به لایه ی ViewModel که در الگوی MVVM ، باعث جدا کردن لایه های View و Model میشه ، هست دیگه؟ درسته؟

دوما ، کلاس Repository درون کدوم لایه قرار میگیره و ارتباط لایه ها با هم چطوری هست؟

سوما ، آیا خود کلاس Repository و اینترفیس هاش ، درون یک لایه ی مجزا قرار میگیرن (فرضا مثل لایه ی Repository Layer در عکس زیر) و کلاس های مربوط به دیتابیس و Entity Framework و موجودیت ها (مثل کلاس Context و موجودیت و کلاس Person در لایه ی Data Layer در عکس زیر) ، درون یک لایه ی مجزای دیگه قرار میگیرن و همچنین منطق تجاری ( Model ( Domain ) Layer در عکس زیر) هم درون یک لایه ی مجزا قرار میگیرن (مثل عکس زیر) ؟
چون قراره که Repository ، این دو را از هم جدا کنه دیگه . پس مثل عکس زیر باید بشن دیگه؟ درسته؟


Repository Pattern.jpg
attachment.php


پس فرضا این (Respository) ، اگه در معماری MVVM اجرا بشه ، اولا Model (یا Domain) با Repository در تماس هست ؟ درسته؟
(یعنی Repository با ViewModel در تماس نیست . هر چند MVVM روی این ارتباط ، انگار تاکید نداره و براش مهم نیست) .

یعنی پروژه ی MVVM ، کلا 5 لایه ی View و ViewModel و Model و Respository و Data میشه دیگه؟ درسته؟

در لینک اول ، شکل اول هم انگار این طوری هست .

=========

- سئوال مهم اینه که اگه قراره که لایه های Model با Data ، فقط توسط Respository ، ارتباط داشته باشن (و این 2 تا لایه اصلا به هم وابسته نباشن) ، پس در همه ی مثال ها (مثل همون لینک های بالا) ، اینترفیس و کلاس مربوط به Respository ، شی ای از کلاسِ موجودیت در لایه ی Data میگیره (شبیه به عکسی که در بالا قرار دادم و متد AddPerson در کلاس PersonRepository ، شی ای از نوع Person که در لایه ی Data قرار داره میگیره) .

و این یعنی اینکه لایه ی قبلی اش (یعنی لایه ی Model) ، برای ارتباط با لایه ی Respository ، مجبوره که شی ای از نوع لایه ی Data بده .
در واقع یعنی باز لایه ی Model ، برای ارتباطط با لایه ی Respository ، مجبوره که به لایه ی Data وابسته بشه .

خوب این مدلی که فلسفه ی Respository را نقض میکنه !
اگه Respository ، جنریک هم باشه ، باز هم فرقی نداره و همین داستان هست .

یا من اشتباه میکنم؟
اگه اشتباه میکنم ، کجا را اشتباه میکنم و داستان Respository چجوری میشه؟

تشکر استاد .
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
خیلی ممنون استاد . :rose:

استاد ، مدت ها قبل ، یکی ، در اون انجمن درباره ی مطلبی پرسید که نهایتا درباره ی async await ها براش توضیح دادم .
این توضیحات را از توضیحاتی که قبلا در اینجا درباره ی این مطلب بهم داده بودید و همچنین از منابع مایکروسافت و بقیه ی منابع گرفتم .

به نظرم اومد چون حداقل در اون انجمن ، گاها پیش میاد که وقتی از مطلبی ، چند سال بگذره ، ممکنه کدهایی که نوشته بودیم ، یه تگ هایی بهش اضافه بشه و خوندنش خیلی سخت بشه (نمیدونم چرا این طوریه و گاها پیش اومد که در اونجا ، به کدم ، تگ های color و اینها اضافه میشد) و همچنین برای یادآوری خودم ، اون مطلب را در این پست هم بذارم (البته اگه توضیحاتم اشکال داره ، ممنون میشم بگیرید) .
تشکر استاد

====================

توضیحات درباره ی Asysnc Await :

سلام
این را هم بعدا برای یادآوری میذارم :

سلام
از async await استفاده کنید :

هنگ برنامه هنگام گرفتن سورس سایت

اما در اون مثال ، 2 نکته را جایگزین کنید :
اول اینکه سعی کنید بجای اون بخش از کد که در نخ جدید اجرا میشه را داده بودم ، یعنی بجای کد زیر در اون مثال :

C#:
    Task<string> httpDataTask = new Task<string>(new Func<string>(this.GetHttpData));
    httpDataTask.Start();

از متدهای استاتیکِ TaskFactory.StartNew یا از Task.Run برای اجرای کدتوی در نخ جدید استفاده کنید :

TaskFactory.StartNew Method (System.Threading.Tasks) | Microsoft Learn

دوم و مهمتر اینکه سعی کنید این تیکه از کدها را وقتی بکار میبرید ، درون متدی بکار ببرید که نوع بازگشتی اش از Task یا Task<T> باشه .
یعنی مثلِ اون مثال ، مستقیما کد بالا را درون رویداد ها (که خروجی ای ندارند و void هستند) ، بکار نبرید .
چون حداقل اینکه برای مدیریت استثنا و خطاهایی که درونِ اون نخِ جدید اتفاق میافته ، نوع خروجی Task یا Task<T> بکار میاد .

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

مدیریت استثناء در نخ جدید

برای اینکار (طبق مثال در لینک بالا) ، کافی هست که بخش await را درون try catch بذارید . اونجایی این کار را میکنید هم توی متدی باید باشه که خروجی اش از نوع Task یا Task<T> باشه .

----------

یه مثال از این نکات بالا :

C#:
        private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)

        {

            long fibonacciValue = await this.FibonacciAsync(37);

            // کد مورد نظرتون برای اینکه بعد از اتمامِ اجرای نخ جدیدتون ، اجرا بشه را اینجا ، یعنی در پایینِ await بنویسید .

            if (fibonacciValue > -1)

                MessageBox.Show(fibonacciValue.ToString());

        }

















        private async Task<long> FibonacciAsync(int number)

        {

            Task<long> fibonucciTask = Task.Factory.StartNew<long>(new Func<object, long>(this.Fibonacci), number);





            long fibonucciReturnValue = -1;

            try

            {

                fibonucciReturnValue = await fibonucciTask;

                // کد مورد نظرتون برای اینکه بعد از اتمامِ اجرای نخ جدیدتون ، اجرا بشه را اینجا ، یعنی در پایینِ await بنویسید .

            }

            catch (Exception exception)

            {

                MessageBox.Show(exception.Message);

            }





            return fibonucciReturnValue;

        }

















        private long Fibonacci(object number)

        {

            int numberConverted = Convert.ToInt32(number);

            if (numberConverted == 0 || numberConverted == 1)

                return numberConverted;





            return this.Fibonacci(numberConverted - 1) + this.Fibonacci(numberConverted - 2);

        }

که خروجیِ await را مستقیما میتونید به عنوان مقدار long در مثال بالا ، برگردونید .
یعنی اگه متدی هم فقط از نوع Task باشه (یعنی Task<T> نباشه) ، دیگه اصلا return کردن ، لازم نداره .


میشه در این سلسله مراتبی هم یه متدی باشه که فقط Task باشه و یعنی هیچ چی برنگردونه .
یعنی مثلا رویداد کلیک در بالا ، یه متدی دیگه ای که در مثال بالا نیست و فرضا اسم اش MiddleMethod باشه که خروجی اش از نوع Task باشه (نه از نوع Task<T> یا Task<long>) را فراخوانی کنه .

بنابراین این متد ، اصلا نیاز به استفاده از کلمه ی کلیدی return در بدنه اش نداره (ربطی به استفاده نکردن از کلمه ی کلیدیِ await نداره و باید ازش استفاده کنه . در واقع توصیه ی اکید میشه که تمام متدهایی که async هستند ، درون شون از await استفاده کنند) .

و این متدِ MiddleMethod ، درون خودش ، متدِ FibonacciAsync در مثال بالا را فراخونی کنه که بقیه ی روال هاش مشخص هست .

---------

await هم کارکردش به این صورت هست که وقتی کنترل برنامه ، به کلمه ی کلیدی await میرسه ، اون Task ئه اجرا شده ای که بهش داده شد را منتظرش میمونه تا اجراش تمام بشه و بنابراین کدهایی که در پایینِ اش نوشته شدن را معلق میذاره (یه کاری شبیه به yield ها) و این کدها اجرا نمیشن تا اون Task خاتمه پیدا کنه ،

و مخصوصا اینکه همچنین کنترلِ اجرای کدها را به استکِ متدِ قبلی هدایت میکنه (یعنی متد قبلی توی استک که فراخوانی کننده ی این متدی بود که توی این متد ، کلمه ی کلیدیِ await وجود داشت ، کنترلِ اجرای کدهای برنامه را به اون استکِ قبلیِ متد در اون نخ میبره) .
یعنی کاری شبیه به return انجام میده (البته از لحاظ برگشتن به استک قبلی ، شبیه بهش هست وگرنه ادامه ی کدهاش بعد از خاتمه ی نخ اجرا میشه که این ، کار را return انجام نمیده) .

-----------

برای مدیریت دسترسی به داده ی واحد (مثلا دسترسی به متغییر سراسری ، یا دسترسی به متغییر محلی ای که اشاره گری به متغییر سراسری هست) درون دو یا چند نخ هم روش های متفاوتی که هر کدوم برای جایی کاربرد دارن ، هست اما یکی از رایج ترین شون ، استفاده از کلمه ی کلیدی lock هست .
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
برای یادآوری :

اصل Liskov substitution principle :

اصل Liskov substitution principle یکی از پنج اصل SOLID در برنامه‌نویسی شی‌گرا است. این اصل می‌گوید که زیرکلاس‌ها باید قابل جایگزینی با کلاس پدرشان باشند، به طوری که هر جایی که یک نمونه از کلاس پدر استفاده شده است، بتوان آن را با یک نمونه از زیرکلاس جایگزین کرد و برنامه همچنان به درستی کار کند.

override کردن یک متد در زیرکلاس و فراخوانی نکردن متد پدر در متد override شده، خود به تنهایی باعث نقض اصل Liskov substitution principle نمی‌شود. یعنی کد زیر و متد Test در کلاس فرزند ، این اصل را نقض نمیکند :

C#:
    public class A
    {
        public virtual string Test()
        {
            return "Father";
        }
    }


    public class B : A
    {
        public override string Test()
        {
            return "Child";
        }
    }

این اصل زمانی نقض می‌شود که رفتار زیرکلاس با رفتار (متد) قرارداد شده در کلاس پدر سازگار نباشد. به عبارت دیگر، اگر override کردن یک متد در زیرکلاس منجر به تغییر رفتارِ متدِ قرارداد شده در کلاس پدر شود، ممکن است باعث نقض اصل Liskov substitution principle شود.

امضای یک متد (شامل نام و نوع پارامترها و نوع خروجی متد) در override کردن یک متد در سی شارپ بایست حفظ شود و تغییر داده نشود.

پس تا زمانى كه يك متد را در كلاس پدر به صورت virtual و در كلاس فرزند به صورت override تعريف كنيم، فقط به خاطر اينكه يك متود را override كرديم، اصل Liskov substitution principle را نقض نكرديم.

مثال نقض اصل Liskov substitution principle :

C#:
public class Rectangle
{
    public virtual int Width { get; set; }
    public virtual int Height { get; set; }

    public int Area()
    {
        return Width * Height;
    }
}

public class Square : Rectangle
{
    private int _sideLength;

    public override int Width
    {
        get => _sideLength;
        set => _sideLength = value;
    }

    public override int Height
    {
        get => _sideLength;
        set => _sideLength = value;
    }
}

در این مثال، کلاس Square از کلاس Rectangle ارث بری می‌کند و خواص Width و Height را override می‌کند. با این حال، رفتار کلاس Square با رفتار قرارداد شده در کلاس Rectangle سازگار نیست. به عنوان مثال، اگر یک نمونه از کلاس Rectangle را با یک نمونه از کلاس Square جایگزین کنیم، برنامه به درستی کار نخواهد کرد:

C#:
Rectangle rectangle = new Square();
rectangle.Width = 4;
rectangle.Height = 5;
int area = rectangle.Area(); // returns 25 instead of 20

در اینجا، با تغییر دادن خواص Width و Height در کلاس Square، رفتار قرارداد شده در کلاس Rectangle تغییر داده شده است و باعث نقض اصل Liskov substitution principle شده است.

برای اجرای اصل Liskov substitution principle در طراحی کلاس‌ها و متدهای شما، می‌توانید به چند نکته زیر توجه کنید:

  • هنگام override کردن یک متد در زیرکلاس، مطمئن شوید که رفتار قرارداد شده در کلاس پدر حفظ شود. به عبارت دیگر، override کردن یک متد نباید باعث تغییر رفتار قرارداد شده در کلاس پدر شود.

  • هنگام تعریف خواص و متدهای جدید در زیرکلاس، مطمئن شوید که این خواص و متدها با خواص و متدهای قبلی سازگار باشند و باعث تغییر رفتار قرارداد شده در کلاس پدر نشوند.

  • هنگام تعریف یک زیرکلاس جدید، بررسی کنید که آیا این زیرکلاس واقعاً یک نوع از کلاس پدر است یا خیر. اگر پاسخ منفی است، به جای ارث بری، ممکن است استفاده از رابطه های دیگر مانند ترکیب (Composition) مناسب‌تر باشد.

  • همچنین مطالعه و بکارگیرى الگوهای طراحى (Design Patterns) كه بيشترشان رعايت كليۀ اصول SOLID را در بُعْديِشان دارند، به شما كمك خواهَد كَرْد تا به صورت ناآگاهانۀ اين اصول را رعايت كُنيد.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
نمونه کد پیاده سازی ساختاری که یک ماژول با استفاده از اصل dependency inversion ، قصد ارتباط با اینترفیس ماژول دیگری را اما در کلاس های سلسله مراتب و نوادگان سطح پایین را دارد که اینترفیس IMainSecond در کد زیر ، این ساختار را نشان میدهد :

C#:
public interface IA
{
    void A();
}

public interface IB
{
    void B();
}

public interface IC
{
    void C();
}

public interface IMain : IA, IB, IC
{
   
}

public class MainFirst : IMain
{
    public void A()
    {
       
    }
   
    public void B()
    {
       
    }
   
    public void C()
    {
       
    }
}

public interface ISecond
{
    string SecondMethod(init par1);
}

public interface IMainSecond : IMain, ISecond
{
   
}

public class MainSecond : IMainSecond
{
    public string SecondMethod(init par1)
    {
        return null;
    }
}

اینترفیسِ IMainSecond ، از دو اینترفیس مجزا مشتق شد ، یا در واقع در اینترفیسِ IMainSecond ، متد SecondMethod را قرار ندادیم چون اصل جداسازی اینترفیس ها در Solid هم رعایت شود و قدرت ماژولاریتی بالاتر رود . وگرنه اشکالی نداشت که این متد را بجای تعریف در اینترفیسِ ISecond ، در اینترفیسِ IMainSecond تعریف میکردیم .
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
برای یادآوری :

بلاک add و remove در رویدادها :

در مورد بلاک add و remove برای رویدادها، این بلاک‌ها به شما امکان می‌دهند تا عملکرد پیش فرض اضافه کردن و حذف کردن یک متد به یک رویداد را کنترل و مدیریت و بازنویسی کنید. به عبارت دیگر، شما می‌توانید تعریف کنید که چه اتفاقی بیفتد زمانی که یک تابع به یک رویداد اضافه یا حذف می‌شود.

برای مثال، شما می‌توانید تعداد دفعاتی که یک تابع به یک رویداد اضافه شده است را شمارش کنید و یا جلوگیری کنید از اینکه یک تابع بیش از یکبار به یک رویداد اضافه شود.

مثالِ رویدادهایی که بلاک add و remove دارند و میتوانیم اضافه یا کم کردن متدهای شان را به Delegate مورد نظر (در مثال شما ، همان دلیگیت _myEvent) کنترل کنیم و در صورت نیاز ، مقدار دلیگیت و متد را به آن رویداد متصل نکنیم ، شبیه به مثال پروپرتی هاست که اکسسور get و set دارند و میتوانیم مقداردهی را برای فیلد مربوط به آن پروپرتی ، کنترل کنیم و در صورت نیاز ، مقدارش را در فیلد مورد نظر نریزیم و مقداردهی نکنیم .

نمونه کد زیر نحوه بازنویسی عملکرد پیش‌فرض اضافه و حذف کردن یک تابع به یک رویداد را نشان می‌دهد:

C#:
public class MyEventClass
{
    private EventHandler _myEvent;

    public event EventHandler MyEvent
    {
        add
        {
            Console.WriteLine("Adding event handler");
            _myEvent += value;
        }
        remove
        {
            Console.WriteLine("Removing event handler");
            _myEvent -= value;
        }
    }

    public void RaiseMyEvent()
    {
        _myEvent?.Invoke(this, EventArgs.Empty);
    }
}
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلام
این را هم بعدا برای یادآوری میذارم :

سلام
از async await استفاده کنید :

هنگ برنامه هنگام گرفتن سورس سایت

اما در اون مثال ، 2 نکته را جایگزین کنید :
اول اینکه سعی کنید بجای اون بخش از کد که در نخ جدید اجرا میشه را داده بودم ، یعنی بجای کد زیر در اون مثال :

یادآوری و نکات تکمیلی درباره ی مدیریت استثنا در async await :


در حالت اجرا بدون استفاده از ویژال استودیو یا با حالت بدون دیباگ اش ، وقتی قسمتِ await را در try catch بذاریم ، exception هایی که در نخ جدید (که همون در Task های اجرا شده هست) پرتاب میکنیم ، هندل و مدیریت میشن .

برای مدیریت خطا در async await ها ، اولا نوع بازگشتی متدی که توی نخ جدیدی اجرا میشه (توسط Task.Run و یا توسط بقیه ی متدها که در نخ جدیدی اجرا میشه) ، خیلی بهتره که از نوع Task (یا Task<T>) باشه .
یعنی نوع بازگشتی این متد ، void نباشه .


C#:
        private async Task MethodAsync()
        {
            Task fibonacciTask = Task.Factory.StartNew(() => { throw new Exception("Test"); });
            try
            {
                await fibonacciTask;
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }

در حالت async await ، وقتی خطایی پرتاب میشه ، اولا اطلاعات خطاها و exception هاش ، در شیِ Task ئه مورد نظرش که توسط متدی که فراخونی اش میکنه ، ذخیره میشه (و دوما ، فقط آخرین خطایی که پرتاب میشه ، بهش دسترسی داریم . نه به کل خطاها).

مثلا
میتوانید exception ای را درون بدنه ی متد Fibonacci پرتاب کنید و بلاک try catch را درون بدنه ی متد (و هندلرِ) button1_Click قرار دهید .

مثال :

C#:
        private async void ButtonBase_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                await M1();
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
           
        }



        public async Task M1()
        {
            await Task.Run(() => Fibonacci("not an int"));
        }


        public long Fibonacci(object obj)
        {
            if (!(obj is int))
            {
                throw new ArgumentException("obj must be of type int");
            }

            return 0;
        }
 

جدیدترین ارسال ها

بالا