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

the_king

مدیرکل انجمن
ممنون استاد علی
ببخشید . اشتباه گفتم
اولا که اون مقدارهایی که آخرشون Key ندارن مثل Control رو Mask میگن و اونایی که دارن مثل ControlKey رو مقدار کلیدی میگن
دوما که برعکس گفتم و درستش میشه :
توی KeyData ، اگه کنترل رو به تنهایی مد نظرمون باشه ، باید هم مقدار کلید و هم مقدار mask اش رو با هم اور کنیم ولی وقتی دکمه ی ترکیبی میشه ، مقدار کلیدی رو نباید چک و اور کنیم! (فقط باید از mask و اون دکمه مثل A و کلا دکمه ی مورد نظرمون استفاده و اور کنیم)
این درسته ؟
نه، ربطی به ترکیب شدن و نشدن نداره، به این بستگی داره که رخداد فشار دادن چه کلیدی باشه، هیچ موقع و تحت هیچ شرایطی در KeyData مقادیر دو تا کلید نمیاد، فقط یک کلید ئه. وقتی دارید رخداد کلید A رو بررسی می کنید دیگه ControlKey نمیتونه داخلش باشه و بالعکس.
وقتی Ctrl رو فشار می دهید خود ControlKey و Control باید باشه و وقتی A رو فشار می دهید (دیگه Ctrl رو قبلا فشار داده اید و رخدادش اتفاق افتاده و رفته پی کارش، الان رخداد فشار دادن A ئه، کاری به Ctrl نداره) برای Ctrl فقط Control رو بررسی می کنید.
لج چیه؟:)
اولا که کاربردشو میدونم . یعنی میدونم اگه مقدار آرایه ای مشخص باشه ، بهتره بجای لیست ها و arraylist ها از آرایه استفاده کنیم و دوما که اگه انواع مختلف داده ای نداشتیم ، بهتره از list ها بجای arraylist ها استفاده کنیم .
فقط برای اطلاعات بیشتر میخوام بدونم
ممنون بابت توضیح کامل متد add و اندیس و count و capacity
اما منظورمو فکر کنم کامل متوجه نشدین
ببینین مثلا یه
arraylist با ظرفیت 5 عضو داریم بنابراین باید بتونیم با مشخص کردن اندیس ایندکسرش ، 5 تا عضو بهش بدیم دیگه؟ درسته؟ چون براش 5 تا خونه ی خالی در نظر گرفتیم . پس چرا نمیشه این کار رو کرد؟ یعنی کد زیر چرا ارور ArgumentOutOfRangeException رو میده؟ :
این درسته ؟


کاملا متوجه منظورتون شدم ولی مفهوم ظرفیت با تعداد اعضاء که یکی نیست. خیر ظرفیت هیچ ربطی به اینکه یهویی عضو 5 امی خلق بشه نداره. شما یک زمین صاف بهتون بدن 100 هکتار، ظرفیت 5 تا خونه رو راحت داره ولی خونه ای که توش نیست، چه پر و چه خالی. تعداد خونه ها 0 ئه. حالا بیاد زمین رو بزرگ کنید بشه 1000 هکتار. بازم ظرفیت هست ولی خونه ای وجود نداره، چه پر و چه خالی. 1000 هکتار زمین خالی دارید ولی خونه های خالی کجای این زمین ئه که بخواهید در خونه چهارم مقدار بدید؟ چیزی وجود نداره.


کد:
            ArrayList arr = new ArrayList(5);
            arr[0] = 3;

این کدی که نوشتید ارتباطی با اون مفهوم Capacity نداره. این چیزی که تغییر دادید Length ئه، مثل Count ئه، براش خونه ایجاد میشه.
گرافیک رو از همون شی موجود بگیریم هم شی جدیدی ایجاد میکنه؟
پس باید شی گرافیک رو از متغییر رویدادی بگیریم دیگه؟
اما چرا پس توی رویداد paint برای کنترل form1 و usercontrol ها اون کدم مشکلی نداشت (مثلا برام فرم ، با همین this.CreateGraphics() شی گرافیک میگرفتم) ؟
کلا CreateGraphics در داخل Paint نباید انجام بشه، هر بارم اجرا بشه Graphics جدید ایجاد میشه، کاری به موارد قبلی و بعدی نداره.
اصولا نباید برای کنترل خودتون Graphics بسازید، باید رسم رو واگذار کنید به Paint. اگه قراره این لحظه رسمی صورت بگیره کنترل رو Invalidate یا Refresh کنید تا Paint رخ بده. هر رسمی هست داخل Paint انجام بدید. Graphics رو جاهایی میسازند که رسمی در کار نیست، مثلا موقعی که قراره روی Bitmap ای چیزی رسم بشه یا قراره ابعاد یک Font ای بررسی بشه و ...

اما چرا پس توی رویداد paint برای کنترل form1 و usercontrol ها اون کدم مشکلی نداشت (مثلا برام فرم ، با همین this.CreateGraphics() شی گرافیک میگرفتم) ؟
بستگی داره که مشکل رو به چی بگید. سه تا حالت پیش میاد، یا رسمی بعد از کد شما انجام نمیشه و تداخلی مشاهده نمیشه (در حالی که از اصل اشکال داره)، یا بعد از رسم شما رسمی صورت می گیره و رسم شما پاک میشه و یا بخاطر عدم دسترسی به منابع اصلا Graphics نمیسازه و کد با خطا متوقف میشه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
ممنون استاد علی
من توی این قضایا ، فقط اون قضیه ی استراکچر , this.Location.X = 4 رو متوجه نشدم که چرا با پروپرتی مشکل داره ولی با کپی کردن در متغییر ، مشکل نداره . درک این قضیه برام مهمه
اگه میشه یه منبعی در این مورد که به این موضوع ربط داشته باشه ، لینک میدین (اگه میشه فارسی باشه)
اگه هم نداشتین که هیچ
------------------------------
درباره ی قضیه ی کیبرد هم KeyData.ToString میکنم و توی لیبل نمایش میدم و دکمه ها رو کلید میکنم تا مقادیرشونو بهم بگه اما انگار آخرین کلیدهای کنترلی رو که فشار میدیم ، مقدار کلیدی رو با خودشون دارن ولی بقیه ی کلیدهای کنترلی ، همون مقدار Mask رو با خودشون دارن به تنهایی (البته انگار در گذشته به مقادیر Mask ، مقادیر Modifire میگفتین)
------------------------------
درباره ی arraylist هم انگار با ایندکسرها نمیشه مقداری توشون قرار داد و با همون متد add باید این کار رو کرد
------------------------------
ممنون . پس از این به بعد گرافیک و رسم در یک کنترل رو با شی گرافیک اش از متغییر رویدادی خودش میسازم
-----------------------------
مشکل آخری هم این اتفاق میافتاد که اصلا رسمی صورت نمیگرفت (با کد قبلی خودم که بهتون دادم)
 

the_king

مدیرکل انجمن
ممنون استاد علی
من توی این قضایا ، فقط اون قضیه ی استراکچر , this.Location.X = 4 رو متوجه نشدم که چرا با پروپرتی مشکل داره ولی با کپی کردن در متغییر ، مشکل نداره . درک این قضیه برام مهمه
اگه میشه یه منبعی در این مورد که به این موضوع ربط داشته باشه ، لینک میدین (اگه میشه فارسی باشه)
اگه هم نداشتین که هیچ

ابهام تون خاصه و عجیبه البته. بذارید مرحله به مرحله بیایم جلو، ببینیم ابهام تون مربوط به کدوم بخش مساله است.
شما در اینکه در کد پایین مقدار a به 2 تغییر پیدا نمی کنه و a همچنان 1 میمونه که مشکلی ندارید؟
کد:
            int a = 1;
            int b = a;
            b = 2;
پس قاعدتا با اینکه در کد پایین مقدار a به (2,2) تغییر پیدا نمی کنه و همچنان (1,1) میمونه هم مشکلی ندارید :
کد:
            Point a = new Point(1, 1);
            Point b = a;
            b.X = 2;
            b.Y = 2;
و در اینکه در کد پایین هم مقدار a همچنان (1,1) میمونه مشکلی ندارید :
کد:
        private Point a = new Point(1, 1);

        private Point GetA()
        {
            return a;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Point b = GetA();
            b.X = 2;
            b.Y = 2;
        }
و در اینکه در کد پایین هم مقدار a همچنان (1,1) میمونه مشکلی ندارید :
کد:
        private Point a = new Point(1, 1);

        private Point A
        {
            get
            {
                return a;
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Point b = A;
            b.X = 2;
            b.Y = 2;
        }
و مشکلی با اینکه در این کد پایین مقدار c همون (1,1) خواهد بود ندارید :
کد:
        private Point a = new Point(1, 1);

        private Point A
        {
            get
            {
                return a;
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Point b = A;
            b.X = 2;
            b.Y = 2;
            Point c = A;
        }
پس قاعدتا با این مساله هم مشکلی ندارید که در get ئه Location یک کپی از مقدار loc ئه return میشه و هر بلایی سر مقدار کپی شده بیارید، تاثیری روی خود loc و مقدارش نداره.
Location + .X + = 4 سه بخشه، اول Location. ارزیابی میشه که میاد یک کپی از مقدار loc رو بر می گردونه. بعد X. ارزیابی میشه، به X اون مقدار کپی شده دسترسی پیدا می کنه که میتونه خونده یا نوشته بشه. بعد 4 = ارزیابی میشه که میخواد مقدار X رو در اون مقدار کپی شده تغییر بده، از نظر فنی عملی ئه ولی این مقدار کپی شده اصلا کجا است؟ یک جای نامعلوم از حافظه که متغیری هم برای دسترسی بهش ندارید. شما بعدا بخواهید ازش استفاده کنید کجا دنبالش می گردید؟ در Location که نمی تونید بگردید، چون هر بار مقدار loc رو return می کنه و چون loc همونه که قبلا بود و X اش هم با اینکارا عوض نمیشه کارتون بیهوده است. شما هر بلایی سر Location.X و Location.Y بیارید هیچ تغییری روی loc صورت نمی گیره، چون از Location فقط کپی مقدار loc ئه که return میشه. خودش عوض نمیشه که. گیرم که شما مقدار X اش رو به 4 تغییر بدید. بعدش چی؟ این مقدار کپی شده کجا است که ازش استفاده کنید؟ هیچ جا. مثل شیء ای است که ساخته باشید ولی چون در هیچ متغیری قرارش نداده اید دیگه بهش دسترسی ندارید و از دست میره. Location.X = 4 یک تغییر لحظه ای ئه که نتیجه اش در یک حافظه موقتی ثبت میشه و بعد از دست میره. هیچ تاثیری هم روی loc و مقدارش نمیذاره.
کامپایلر به این جهت جلوی اینکار رو می گیره که کاری که می کنید بیخودیه، منطق درستی نداره. همونطور که جلوی ;int i; int i رو میگیره، تعریف مجدد i به همون شکل قبلی مشکلی ایجاد نمی کنه، اما چون بیخودیه با خطا جلوشو می گیره.

درباره ی قضیه ی کیبرد هم KeyData.ToString میکنم و توی لیبل نمایش میدم و دکمه ها رو کلید میکنم تا مقادیرشونو بهم بگه اما انگار آخرین کلیدهای کنترلی رو که فشار میدیم ، مقدار کلیدی رو با خودشون دارن ولی بقیه ی کلیدهای کنترلی ، همون مقدار Mask رو با خودشون دارن به تنهایی (البته انگار در گذشته به مقادیر Mask ، مقادیر Modifire میگفتین)

Keys.Modifiers خودش یک مقدار ئه (The bitmask to extract modifiers from a key value) که ترکیب مقدار تمامی Mask هاست، وقتی ازش استفاده میشه که بخواهیم همه Mask های داخل مقدار رو یکجا تفکیک کنیم. گفتم که، هر کلیدی که فشار بدید چه کنترلی باشه و چه نباشه برای خودش یک رخداد KeyDown اتفاق می افته که مقدار Key اش در KeyData هست. صحبت آخرین و اولین نیست، صحبت کلیدی ئه که رخداد KeyDown الان بخاطر فشار دادنش در این لحظه رخ داده. اگه در گذشته فشار داده شده بود که دیگه این رخداد KeyDown این لحظه برای اون نیست.


درباره ی arraylist هم انگار با ایندکسرها نمیشه مقداری توشون قرار داد و با همون متد add باید این کار رو کرد

بدیهی ئه، اگه میخواد عضو جدید به مجموعه اضافه کنید از Add استفاده می کنید، اگر هم بخواهید مقدار یک عضو موجود رو عوض کنید با همون ... = arraylist[0] میتوانید اینکار رو بکنید. کاری که می کنید برای این اشتباهه که دارید به عضو با اندیسی که وجود نداره مقدار می دهید، وگرنه اگه همچین اندیسی وجود داشته باشه تغییر دادن مقدارش هیچ ایرادی نداره.
 

SU-57

Active Member
آقا سجاد چرا این کد ها کار نمی کنن

در کد اول طبق گفته شما باید تبدیل صورت بگیره و چون Int16 تا 32767 رو می تونه پشتیبانی کنه پس باید این عدد رو نشون بده اما 1- نشون میده (منظورم اون جمله ای است که گفتی مقداری از داده از دست میره)


کد:
  Int32 A = 65535;
            Int16 B = (Int16)A;
          MessageBox.Show(B.ToString());
خودم هم که با کلاس convert تبدیل می کنم یک پنجره ای میاد توش نوشته break و continue

Int32 A = 65535;
Int16 B = Convert.ToInt16(A);
MessageBox.Show(B.ToString());

(ببخشید دکمه درج کد تو ویرایش کار نمی کنه اون کد بالا رو هم چپ چین کردم نمی دونم چرا کد ها رو ردیف هم نمی نویسه)

******

بعد اینکه آقا سجاد شما گفتی که کامپایلر به طور مخفی تبدیل می کنه یا implicitly که در یک جا هم اشاره کردی به طور ضمنی. تو گوگل معنیش رو نوشته به طور ضمنی و اصلا به طور مخفی ننوشته. من اصلا این مفهوم رو متوجه نشدم که به طور ضمنی یعنی چه. اصل روش رو فهمیدم اما این کلمه ضمنی رو هر چی فکر کردم متوجه نشدم.
 
آخرین ویرایش:

SajjadKhati

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

در کد اول طبق گفته شما باید تبدیل صورت بگیره و چون Int16 تا 32767 رو می تونه پشتیبانی کنه پس باید این عدد رو نشون بده اما 1- نشون میده (منظورم اون جمله ای است که گفتی مقداری از داده از دست میره)


کد:
  Int32 A = 65535;
            Int16 B = (Int16)A;
          MessageBox.Show(B.ToString());
چون در نوع داده ای Int16 ، عدد منفی رو هم حساب میکنه . اعداد منفی هم که بخوان حساب شن ، آخرین بیت رو به اعداد منفی اختصاص میدن یعنی یک بیت کم میشه (برای محاسبه ی اعداد منفی) پس بجای 16 بیت ، 15 بیت جا میگیره . توی 15 بیت هم ، حداکثر تا عدد 32767 جا میگیره
برای اینکه همه ی اعداد رو مثبت حساب کنه (یعنی عدد منفی رو قبول نکنه) یا در واقع ، آخرین بیت رو عدد منفی حساب نکنه ، تا در همه ی 16 بیت اش ، عدد مثبت قرار بگیره ، باید عدد 65535 رو توی نوع داده ای UInt16 بریزین :

کد:
            Int32 A = 65535;
            UInt16 B = (UInt16)A;
            MessageBox.Show(B.ToString());
البته این مسائل توی آموزش بررسی شد

خودم هم که با کلاس convert تبدیل می کنم یک پنجره ای میاد توش نوشته break و continue
متوجه نشدم منظورتون چیه

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

کد:
            Int32 a = 5;
            Int64 b = a;
متغییر a ، وقتی میخواد توی متغییر b که از جنس Int64 هست ریخته شه ، حتما اول باید به نوع Int64 تبدیل شه و بعد بتونه توش قرار بگیره ولی ما در کد بالا ، تبدیلی انجام ندادیم . این تبدیل رو کمپایلر انجام میده . یعنی کمپایلر ، مخفیانه کد بالا رو به شکل زیر ویرایش میکنه :
کد:
            Int32 a = 5;
            Int64 b = (Int64)a;
ولی کمپایلر زمانی خودش تبدیل میکنه که اطمینان کامل داشته باشه که مقداری از دست نمیره . یعنی در کد زیر ، متغییر a رو دیگه خودش تبدیل نمیکنه :
کد:
            Int64 a = 5;
            Int32 b = a;
چون میگه ممکنه طرف توی متغییر a ، یک عددی که بیشتر از 32 بیت اشغال کنه رو قرار داده باشه که اگه در این صورت تبدیل به 32 بیت کنه ، مقادیر از دست میره . در این صورت ، تبدیل نوع رو به خود برنامه نویس واگذار میکنه . این تبدیل نوعی که برنامه نویس انجام میده رو ، تبدیل آشکار میگن
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
ابهام تون خاصه و عجیبه البته. بذارید مرحله به مرحله بیایم جلو، ببینیم ابهام تون مربوط به کدوم بخش مساله است.
شما در اینکه در کد پایین مقدار a به 2 تغییر پیدا نمی کنه و a همچنان 1 میمونه که مشکلی ندارید؟
کد:
            int a = 1;
            int b = a;
            b = 2;
پس قاعدتا با اینکه در کد پایین مقدار a به (2,2) تغییر پیدا نمی کنه و همچنان (1,1) میمونه هم مشکلی ندارید :
کد:
            Point a = new Point(1, 1);
            Point b = a;
            b.X = 2;
            b.Y = 2;
و در اینکه در کد پایین هم مقدار a همچنان (1,1) میمونه مشکلی ندارید :
کد:
        private Point a = new Point(1, 1);

        private Point GetA()
        {
            return a;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Point b = GetA();
            b.X = 2;
            b.Y = 2;
        }
و در اینکه در کد پایین هم مقدار a همچنان (1,1) میمونه مشکلی ندارید :
کد:
        private Point a = new Point(1, 1);

        private Point A
        {
            get
            {
                return a;
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Point b = A;
            b.X = 2;
            b.Y = 2;
        }
و مشکلی با اینکه در این کد پایین مقدار c همون (1,1) خواهد بود ندارید :
کد:
        private Point a = new Point(1, 1);

        private Point A
        {
            get
            {
                return a;
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Point b = A;
            b.X = 2;
            b.Y = 2;
            Point c = A;
        }
پس قاعدتا با این مساله هم مشکلی ندارید که در get ئه Location یک کپی از مقدار loc ئه return میشه و هر بلایی سر مقدار کپی شده بیارید، تاثیری روی خود loc و مقدارش نداره.
Location + .X + = 4 سه بخشه، اول Location. ارزیابی میشه که میاد یک کپی از مقدار loc رو بر می گردونه. بعد X. ارزیابی میشه، به X اون مقدار کپی شده دسترسی پیدا می کنه که میتونه خونده یا نوشته بشه. بعد 4 = ارزیابی میشه که میخواد مقدار X رو در اون مقدار کپی شده تغییر بده، از نظر فنی عملی ئه ولی این مقدار کپی شده اصلا کجا است؟ یک جای نامعلوم از حافظه که متغیری هم برای دسترسی بهش ندارید. شما بعدا بخواهید ازش استفاده کنید کجا دنبالش می گردید؟ در Location که نمی تونید بگردید، چون هر بار مقدار loc رو return می کنه و چون loc همونه که قبلا بود و X اش هم با اینکارا عوض نمیشه کارتون بیهوده است. شما هر بلایی سر Location.X و Location.Y بیارید هیچ تغییری روی loc صورت نمی گیره، چون از Location فقط کپی مقدار loc ئه که return میشه. خودش عوض نمیشه که. گیرم که شما مقدار X اش رو به 4 تغییر بدید. بعدش چی؟ این مقدار کپی شده کجا است که ازش استفاده کنید؟ هیچ جا. مثل شیء ای است که ساخته باشید ولی چون در هیچ متغیری قرارش نداده اید دیگه بهش دسترسی ندارید و از دست میره. Location.X = 4 یک تغییر لحظه ای ئه که نتیجه اش در یک حافظه موقتی ثبت میشه و بعد از دست میره. هیچ تاثیری هم روی loc و مقدارش نمیذاره.
کامپایلر به این جهت جلوی اینکار رو می گیره که کاری که می کنید بیخودیه، منطق درستی نداره. همونطور که جلوی ;int i; int i رو میگیره، تعریف مجدد i به همون شکل قبلی مشکلی ایجاد نمی کنه، اما چون بیخودیه با خطا جلوشو می گیره.
آها . ممنون استاد علی . متوجه شدم . البته توی آموزش هم گفتم
اگه اشتباه نکنم ، من این جوری درک کردم :
اولا که هر وقت یک پروپرتی ای میخواد خونده شه ، از فیلد مورد نظرش اطلاعات رو میخونه و چون از نوع استراکچر هستن ، پس اول کپی ای از مقدار فیلد توی پروپرتی ریخته میشه و اون حافظه ی کپی شده هست که برگردونده میشه
حالا اگه قرار به نوشتن باشه (از طریق پروپرتی) ، فرضا هم که توی حافظه ی کپی شده ای که در پروپرتی هست ، بنویسه ولی دوباره موقع فراخونی ، پروپرتی اطلاعات شو از حافظه ی متغییر میگیره پس واسه همین تغییر اعضای یه استراکچر توسط پروپرتی ، توسط کمپایلر گیر داده میشه
درسته (دیگه :)


Keys.Modifiers خودش یک مقدار ئه (The bitmask to extract modifiers from a key value) که ترکیب مقدار تمامی Mask هاست، وقتی ازش استفاده میشه که بخواهیم همه Mask های داخل مقدار رو یکجا تفکیک کنیم. گفتم که، هر کلیدی که فشار بدید چه کنترلی باشه و چه نباشه برای خودش یک رخداد KeyDown اتفاق می افته که مقدار Key اش در KeyData هست. صحبت آخرین و اولین نیست، صحبت کلیدی ئه که رخداد KeyDown الان بخاطر فشار دادنش در این لحظه رخ داده. اگه در گذشته فشار داده شده بود که دیگه این رخداد KeyDown این لحظه برای اون نیست.
ممنون
بدیهی ئه، اگه میخواد عضو جدید به مجموعه اضافه کنید از Add استفاده می کنید، اگر هم بخواهید مقدار یک عضو موجود رو عوض کنید با همون ... = arraylist[0] میتوانید اینکار رو بکنید. کاری که می کنید برای این اشتباهه که دارید به عضو با اندیسی که وجود نداره مقدار می دهید، وگرنه اگه همچین اندیسی وجود داشته باشه تغییر دادن مقدارش هیچ ایرادی نداره.
پس چرا وقتی با شی ای از کلاس Dictionary استفاده میکنیم ، میتونیم بصورت مستقیم ، با ایندکس ها مقداردهی شون کنیم؟ و ارور هم نمیدن؟ (یعنی لازم نیست با متد add ، حتما پرشون کنیم) :
کد:
            Dictionary<int, string> a = new Dictionary<int, string>();
            a[0] = "salam";

-----------------------------------

سئوال بعدی اینکه ، من نسخه ی 15.3 ویژال استودیو رو توی ویندوز 7 نصب کردم و نسخه ی 15.2 شو توی ویندوز 10
حالا دیروز که توی ویندوز 7 نصب کردم و همون پروژه ای رو که از ویندوز 10 بالا آوردم رو ، بالا آوردم و یه لحظه در پروپرتیس پروژه ، نسخه ی دات نت شو عوض کردم (اما دوباره برگردوندم به نسخه ی قبل) ، اما از اون به بعد ، دیگه حالت desine از usercontrol ام (که اسمش page2 بود) ، دیگه بالا نمیاد (فقط محیط کدنویسی اش میاد) ولی usercontrol page1 ام مشکلی نداره! page2 ام که usercontrol بود (شکل آیکون اش شبیه فرم بود) ، از اون به بعد به آیکون سبز رنگ #C تغییر آیکون داد ولی page1 ام همون موند.
نمیشه درستش کرد و حالت desine در page2 رو برگردوند؟
 
آخرین ویرایش:

SU-57

Active Member
آقا سجاد این کد رو می گم که break و continue میاد

کد:
 Int32 A = 65535;
            Int16 B = Convert.ToInt16(A);
            MessageBox.Show(B.ToString());

بعد شما این کد رو نوشتی (ببخش که دکمه درج کار نمی کنه فقط یکبار هر دفعه می شه کد نوشت بنابراین من عکس میزارم)

عکس1

به نظرت اگه از اول کد رو به صورت زیر بنویسیم بهتر نیست که دردسر تبدیل نداشته باشیم و شلوغ کاری هم نشه

عکس2

****

بعد اینکه شما چرا از underline در اسامی استفاده می کنی این اولی که با خط فاصله است به نظرت قشنگ تر نیست؟

عکس3
 

SajjadKhati

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

کد:
 Int32 A = 65535;
            Int16 B = Convert.ToInt16(A);
            MessageBox.Show(B.ToString());

بعد شما این کد رو نوشتی (ببخش که دکمه درج کار نمی کنه فقط یکبار هر دفعه می شه کد نوشت بنابراین من عکس میزارم)
من متوجه نمیشم
کدوم قسمت و کدوم دقیقه و چی شده؟
کد بالا که ربطی به دستور break و continue نداره !
بعد از ویرایش ، دکمه ی "ویرایش پیشرفته" رو بزنین تا بتونین کد اضافه کنین


عکس1

به نظرت اگه از اول کد رو به صورت زیر بنویسیم بهتر نیست که دردسر تبدیل نداشته باشیم و شلوغ کاری هم نشه

عکس2

****

بعد اینکه شما چرا از underline در اسامی استفاده می کنی این اولی که با خط فاصله است به نظرت قشنگ تر نیست؟

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

SU-57

Active Member
من متوجه نمیشم
کدوم قسمت و کدوم دقیقه و چی شده؟
کد بالا که ربطی به دستور break و continue نداره !
آقا شما یک button ایجاد کن و این کد رو بذار داخلش با رویداد کلیک

کد:
Int32 A = 65535;
  Int16 B = Convert.ToInt16(A);
  MessageBox.Show(B.ToString());

پیغام زیر میاد

عکس
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
آقا شما یک button ایجاد کن و این کد رو بذار داخلش با رویداد کلیک

کد:
Int32 A = 65535;
  Int16 B = Convert.ToInt16(A);
  MessageBox.Show(B.ToString());

پیغام زیر میاد

عکس

مشخص هست دیگر گلپسر
بخاطر اینکه اون عددی که در متغییر A ریختین ، 16 بیت کامل رو پر کرد اما متغییر B بخاطر اینکه اعداد منفی رو قبول میکنه ، 15 بیت فقط خونه داره . بیت آخرش یعنی بیت شونزدهم اش برای اعداد منفی هه .
یه عدد 16 بیت (متغییر A) رو هم نمیشه ریخت توی متغییر 15 بیتی (متغییر B)
توی قسمت 4 این مسئله گفته شد . توی پست قبل هم که این قضیه رو توضیح دادم
 

the_king

مدیرکل انجمن
آها . ممنون استاد علی . متوجه شدم . البته توی آموزش هم گفتم
اگه اشتباه نکنم ، من این جوری درک کردم :
اولا که هر وقت یک پروپرتی ای میخواد خونده شه ، از فیلد مورد نظرش اطلاعات رو میخونه و چون از نوع استراکچر هستن ، پس اول کپی ای از مقدار فیلد توی پروپرتی ریخته میشه و اون حافظه ی کپی شده هست که برگردونده میشه
حالا اگه قرار به نوشتن باشه (از طریق پروپرتی) ، فرضا هم که توی حافظه ی کپی شده ای که در پروپرتی هست ، بنویسه ولی دوباره موقع فراخونی ، پروپرتی اطلاعات شو از حافظه ی متغییر میگیره پس واسه همین تغییر اعضای یه استراکچر توسط پروپرتی ، توسط کمپایلر گیر داده میشه
درسته (دیگه :)
حالا درست شد.
پس چرا وقتی با شی ای از کلاس Dictionary استفاده میکنیم ، میتونیم بصورت مستقیم ، با ایندکس ها مقداردهی شون کنیم؟ و ارور هم نمیدن؟ (یعنی لازم نیست با متد add ، حتما پرشون کنیم) :
کد:
            Dictionary<int, string> a = new Dictionary<int, string>();
            a[0] = "salam";
این دیگه قانون نداره، به دید طراحش بستگی داره، برای همین گفتم در مورد بعضی کلاس ها ممکنه صدق کنه. بعضی متد ها اگه درست کار نکنند null یا 1- بر می گردونند، بعضی های دیگه throw Exception می کنند، این بستگی به دید طراحشون داره. تو خود NET. هم همه چی الزاما با یک دید خاص و مشابه نیست. چه برسه به اینکه طراحشون از شرکت دیگه ای باشه.
سئوال بعدی اینکه ، من نسخه ی 15.3 ویژال استودیو رو توی ویندوز 7 نصب کردم و نسخه ی 15.2 شو توی ویندوز 10
حالا دیروز که توی ویندوز 7 نصب کردم و همون پروژه ای رو که از ویندوز 10 بالا آوردم رو ، بالا آوردم و یه لحظه در پروپرتیس پروژه ، نسخه ی دات نت شو عوض کردم (اما دوباره برگردوندم به نسخه ی قبل) ، اما از اون به بعد ، دیگه حالت desine از usercontrol ام (که اسمش page2 بود) ، دیگه بالا نمیاد (فقط محیط کدنویسی اش میاد) ولی usercontrol page1 ام مشکلی نداره! page2 ام که usercontrol بود (شکل آیکون اش شبیه فرم بود) ، از اون به بعد به آیکون سبز رنگ #C تغییر آیکون داد ولی page1 ام همون موند.
نمیشه درستش کرد و حالت desine در page2 رو برگردوند؟
قطعا میشه ولی چیزی نیست که همینطوری یه نکته بگم لحظه ای ایراد رو پیدا کنید.
دو تا چیز معمولا بد عوض میشه، یکی یکسری using ها است که ممکنه تو یه نسخه NET. باشه تو یه نسخه دیگه نباشه که وقتی برمیگردونید به نسخه قبلی دیگه خودکار حذف نمیشن یا نسخه شون از نسخه بالاتر بر نمیگرده به نسخه قبلی و درست link نمیشن. یک مورد دیگه نسخه resource اون Usercontrol ها است که ورژن میره بالا ولی دیگه نمیاد پایین، فرمت resx ها ورژن داره. تو حالت متنی اون فایل های جانبی اش رو باز کنید و با فایل های یه UserControl جدید مقایسه کنید تا تفاوت ها پیدا بشه. راه حل مرحله به مرحله مشخصی نمیتونم بهتون بگم که فلان کار رو بکنید فوری درست بشه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
کلاس فرقی نداره اما برای دسترسی protected بین ایندو فرق وجود داره، protected به شما مجوز دسترسی به اون base رو میده نه همه Form1 ها.

protected کاری به این نداره که فلان شیء از چه نوع کلاسی ئه، دسترسی هر instance اش صرفا برای اشیاء وارث همون instance محدود شده، شما از یک instance وارث دیگه نمی توانید به متغیری دسترسی داشته باشید که از یک instance دیگه است، حتی اگه از همون نوع کلاس باشه.
این تعریف دسترسی protected ئه، وگرنه مساله تفاوت کلاس base با هر Test1 ای نیست. شما با base و بدون base صرفا به myDinamicVar4 ای دسترسی دارید که برای همون شیء Test1 وجود داره و کد داخلش اجرا میشه. اون شیء که شما از کلاس form1Obj ساخته اید، از جنس کلاس والد Test1 ئه ولی این شیء هیچ ارتباطی با دسترسی protected برای شیء ای که شما اون کد رو داخل اجرا می کنید نداره، برای همین protected برای دسترسی اون کد به اون form1Obj.myDinamicVar4 کافی نیست. این محدودیت protected ئه.

سلامی چو بوی گل روشنایی :green: استاد علی .
اولا که درباره ی جواب پست بالاتون ، ممنون
دوم اینکه ، اینو توی صفحه ی 11 گفته بودین
منظورتون از متن قسمتی که بولد کردم ، اینه که اگه شی جدیدی در کلاس های فرزندانِ کلاسی که عضو Protected در اون هست ، ساخته بشه ، از طریق اون شی ، به اعضای Protected کلاس پدر ، دسترسی نداریم و فقط از طریق اشیاء this و base در کلاس های ارث بری شده ، دسترسی وجود داره؟
اگه منظورتون اینه ، پس چرا وقتی درون کلاس فرزند ، از خود همون کلاس فرزند ، شی جدید میسازیم ، به اعضای Protected کلاس پدر ، دسترسی داریم ولی اگه از درون کلاس فرزند ، شی جدیدی از کلاس پدر بسازیم ، چرا دیگه اعضای Protected کلاس پدر ، در دسترس نیست؟
یعنی دسترسی به اعضای Protected در کلاس هایی که وراثت رو رعایت کردن ، نباید با شی جدید ساختن در این کلاس ها ، مشکل داشته باشن . انگار فقط در صورتی که شی جدید اونم از درون کلاس فرزند باشیم و شی جدیدی از کلاس پدر یا اجداد بسازیم ، مشکل دسترسی به اعضای Protected کلاس پدر و اجداد رو خواهیم داشت
چرا؟!
 

the_king

مدیرکل انجمن
سلامی چو بوی گل روشنایی :green: استاد علی .
اولا که درباره ی جواب پست بالاتون ، ممنون
دوم اینکه ، اینو توی صفحه ی 11 گفته بودین
منظورتون از متن قسمتی که بولد کردم ، اینه که اگه شی جدیدی در کلاس های فرزندانِ کلاسی که عضو Protected در اون هست ، ساخته بشه ، از طریق اون شی ، به اعضای Protected کلاس پدر ، دسترسی نداریم و فقط از طریق اشیاء this و base در کلاس های ارث بری شده ، دسترسی وجود داره؟
اگه منظورتون اینه ، پس چرا وقتی درون کلاس فرزند ، از خود همون کلاس فرزند ، شی جدید میسازیم ، به اعضای Protected کلاس پدر ، دسترسی داریم ولی اگه از درون کلاس فرزند ، شی جدیدی از کلاس پدر بسازیم ، چرا دیگه اعضای Protected کلاس پدر ، در دسترس نیست؟
یعنی دسترسی به اعضای Protected در کلاس هایی که وراثت رو رعایت کردن ، نباید با شی جدید ساختن در این کلاس ها ، مشکل داشته باشن . انگار فقط در صورتی که شی جدید اونم از درون کلاس فرزند باشیم و شی جدیدی از کلاس پدر یا اجداد بسازیم ، مشکل دسترسی به اعضای Protected کلاس پدر و اجداد رو خواهیم داشت
چرا؟!
هدف اصلی این محدودیت ها چیه که یه چیزی رو فرضا بیان private تعریف کنن؟ چرا آزادی مطلق ندن که همه چی public باشه؟ هدف اصلی این محدودیت ها رعایت یکی از اصول اساسی شی گرایی است که میگه باید مخفی سازی یا کپسوله سازی صورت بگیره، یعنی تا جایی که میشه چیزی که به کسی ربط نداره ازش مخفی کنید تا درگیرش نباشه و ساده تر مدیریت بشه. به چیزی دسترسی نداشته باشه که بهش ربطی نداره و نباید درگیرش باشه.

حالا شما تصور کنید، من سازنده کلاس A هستم، برای منظوری لازمه اجازه بدم از A وراثتی باشه و لازمه بعضی اعضاء A در وراثت قابل دسترسی وارث باشند. نه private مناسبه که به کسی خارج از A اجازه دسترسی نمیده و نه public مناسبه که به همه اجازه دسترسی میده. یک چیزی میان اینها لازمه.
از طرف دیگه مجوز دسترسی نباید شکاف در دسترسی های داخلی تر ایجاد کنه. یعنی فرضا اگه پدر بزرگ عضوی داره که private ئه، معنیش اینه که نمیخواد هیچکسی جز نوع خودش بهش دسترسی داشته باشه، حتی پسرش، حتی وارثینش.
شما ممکنه با حالت های مختلف این وراثت رو محدود کنید، با مخفی کردن یا محدود کردن متد سازنده اش یا با sealed کردن کلاس و ...
حالا تصور کنید دسترسی protected بهتون اجازه میداد که هر زمان خواستید از اعضاء protected یک A ای استفاده کنید الکی از کلاسش وراثت بگیرید در حالی که اصلا قصد استفاده از base خودش رو ندارید، وراثت گرفته اید تا سراغ A دیگری بروید که وارثش نیستید. این با اصل ماهیت این محدودیت ها جور در نمیاد. این محدودیت ها برای اینه که اگه لازمه جایی چیزی رو مخفی کنید با بیشترین تنوع محدودیت اینکار انجام بشه، نه اینکه دسترسی ها ساده و بدون محدودیت باشه. وگرنه همه چیز رو public می گرفتند. protected میخواد تا حد امکان دسترسی ها رو محدود کنه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
اصلا where myType :string معنی نداره، myType اگر قرار باشه یک نوع داده ثابت باشه که دیگه Generic نیست و با <myType> نوشته نمیشه، بجای myType صریحا string رو می نویسند. شما می توانید بگید myType با string سازگار باشه، روی string کلید F12 رو فشار بدید، همه وراثت هایش اعم از IComparable و ICloneable و.. رو که بنویسید با string سازگار میشه. اما نمی توانید بگویید Generic باشه ولی فقط string باشه.

ممنون استاد علی
استاد علی ، اینو توی صفحه ی 12 گفتین
انگار کلاس هایی که seald نباشن ، توی محدودیت ایجاد کردن در Generic ها مشکلی ندارن مثل کلاس button یا treenode و ... :

کد:
        public type1 Generic<type1>(type1 par1) where type1 : Button, new()
        {
            return new type1();
        }

ولی طبق گفته تون ، برای string ها ، اینترفیس هایی که به ارث برد رو گذاشتم :


کد:
        public type1 Generic<type1>(type1 par1) where type1 : IComparable, ICloneable, IConvertible, IEnumerable, IComparable<String>, IEnumerable<char>, IEquatable<String>, new()
        {
            return new type1();
        }

تعریف متد ، مشکلی نداره ولی موقع فراخونی ، اشکال زیر رو میگیره :


کد:
Severity    Code    Description    Project    File    Line    Suppression State
Error    CS0310    'string' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'type1' in the generic type or method 'Page2.Generic<type1>(type1)'    Practice 1    E:\Project\Visual Studio\C#.Net\Saved Project\Practice 1\Practice 1\MyUserControl\For Main Form\Page2.cs    409    Active

چرا؟
-------------------------
بعد اینکه وقتی میخوایم محدودیت بذاریم ، فقط برای یک کلاس اما از چندین اینترفیس میشه برای محدودیت استفاده کرد دیگه؟ (مثل ارث بری) . درسته؟ یعنی نمیشه همزمان برای 2 کلاس (مثلا Button و TreeNode با هم) محدودیت ایجاد کرد. درسته؟
------------------------
مشکل اینکه نمیذاره از کلاس های seald شده در محدودیت استفاده کنیم ، چیه؟


کلا نوع رابطه با اون چیزی که تصور می کنید متفاوته، where myType :string عملگر : داره نه = ، نمیخواد بگه myType چه مقادیری میتونه باشه، میخواد مشخص کنه که از نظر وراثت اش چه ویژگی ای داره. string که sealed ئه و نمیشه ازش وراثتی داشت، int هم که struct ئه و struct ها جزو موارد قابل وراثت نیستند. شما هر نوع struct ای رو در هر صورت و هر کلاسی که sealed باشه نمی توانید برای : where T بکار ببرید.

منظورتون از جمله ی :
"نمیخواد بگه myType چه مقادیری میتونه باشه، میخواد مشخص کنه که از نظر وراثت اش چه ویژگی ای داره"
و دقیقا کلمه ی "ویژگی ای" ، چیه؟
--------------------
تازه تست کردم ! struct ها چرا بصورت پیش فرض و توی خودشون sealed ان؟!
 
آخرین ویرایش:

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
یه جای کار تون اشتباهه، مثلا موقعیتی که MessageThread1 رو تعریف کردید کنار همون btnThread1_Click ئه؟ چون کد کامل رو نمی بینم نمیتونم نظری بدم.


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


اگه بخواهید بعدا اجرا از یک موقعیت خاص ادامه پیدا کنه باید در کد داخل Thread یکجا معطل اش یک موردی بکنیدش که به اصطلاح suspend بشه، چیزی که شما می خواهید Suspend ئه که Thread هنوز عمرش تموم نشده ولی در حالت معوق قرار گرفته و در حال اجرا نیست. همون قضیه استفاده از lock و Monitor و Mutex و AutoResetEvent و ManualResetEvent و Semaphore و ...
کد:
        private delegate void SetTextDelegate(string text);
        private System.Threading.ManualResetEvent _event;
        private System.Threading.Thread _thread;

        private void buttonStart_Click(object sender, EventArgs e)
        {
            var start = new System.Threading.ThreadStart(Test);
            _thread = new System.Threading.Thread(start);
            _event = new System.Threading.ManualResetEvent(true);
            _thread.Start();
        }

        private void buttonPause_Click(object sender, EventArgs e)
        {
            _event.Reset();
        }

        private void buttonResume_Click(object sender, EventArgs e)
        {
            _event.Set();
        }

        private void buttonDestroy_Click(object sender, EventArgs e)
        {
            _thread.Abort();
        }

        private void Test()
        {
            for (int i = 0; ; i++)
            {
                _event.WaitOne();
                Invoke(new SetTextDelegate(SetText), i.ToString());
                System.Threading.Thread.Sleep(200);
            }
        }

        private void SetText(string text)
        {
            this.Text = text;
        }
در مثال بالا 4 چهار تا دکمه Start و Pause و Resume و Destroy برای کنترل Thread روی فرم قرار می دهید. وقتی Thread در حال اجرا است یک شمارنده در عنوان فرم شروع به کار می کنه و با event_ اجراش رو موقتا متوقف می کنید و یا به حالت اجرا بر می گردونید. هر جا که اجرا قابل توقف باشه باید با event.WaitOne_ اجازه بگیرید، اگر event_ در حالت Set یا همون true باشه اجازه دارید و اجرا ادامه پیدا می کنه ولی وقتی false باشه که متد Reset اینکار رو انجام میده Thread به حالت suspend میره تا زمانی که از جایی event_ مجددا Set بشه. نکته مهم اینجا است که شما صرفا روی Thread اصلی فرم اجازه کار کردن با اغلب کنترل های فرم رو دارید و بجز یکسری مقادیر که در هر حال قابل دسترسی اند در سایر موارد نباید از یک Thread دیگه سراغ کنترل ها بروید، مثلا عنوان فرم رو نمی توانید مستقیما از یک Thread دیگه عوض کنید چون با خطای دسترسی متوقف میشه. اون delegate ئه SetTextDelegate و اون متد SetText برای همین منظوره. با Invoke از Thread اصلی فرم درخواست می کنید که به نیابت از Thread شما اون متد SetText رو اجرا کنه، یعنی اجرا کننده SetText اون Thread ای که شما ساخته اید نیست، اینکار رو می کنید چون داخل SetText عنوان فرم رو عوض می کنید که دسترسی اش رو از داخل Thread تون ندارید. Invoke بدون وجود یک delegate که بگه این متد چه prototype ای داره عملی نیست، به کمک اون delegate بهش می فهمونید که SetText یک متد void ئه که یک پارامتر string داره. در فروم اگه دنبال BackgroundWorker و Invoke بگردید در این مورد قبلا در فروم صحبت شده.

ممنون استاد علی
اینو توی صفحه ی 12 گفتین
میشه بجای استفاده از کلاس ManualResetEvent ، از همون اعضای کلاس Thread برای مکث و ادامه و استوپ دادن کدها استفاده کرد دیگه؟ درسته؟
فرق استفاده از این دو کلاس برای مکث و .. .چیه مگه که شما ازش استفاده کردین؟ انگار سرعت اجرای کد با این کلاس ، کمتر هم هست!
 

the_king

مدیرکل انجمن
ممنون استاد علی
اینو توی صفحه ی 12 گفتین
میشه بجای استفاده از کلاس ManualResetEvent ، از همون اعضای کلاس Thread برای مکث و ادامه و استوپ دادن کدها استفاده کرد دیگه؟ درسته؟
فرق استفاده از این دو کلاس برای مکث و .. .چیه مگه که شما ازش استفاده کردین؟ انگار سرعت اجرای کد با این کلاس ، کمتر هم هست!
اساسا نخ باید در جاهای مشخصی متوقف بشه، نه هر جایی. و شما در نخ کنترل کننده هم باید بدونید و بتونید کنترل کنید که کجا وایسه. معمولا در نخ ها یکسری منابع مشترک هست، یکسری هماهنگی بین نخ ها هست، مثل اینه که ماشینی رو وسط اتوبان یهو suspend کنید، یا یک منبعی مشترکی رو که قراره یک لحظه بدید دست یک نخ رو بدید و هنوز آزادش نکرده suspend اش کنید. اینها همه شون مساله است و ایراد ئه. ویژگی ManualResetEvent اینه که بجای اینکه یهو پریز نخ رو وسط یک کار مهم که باید تا ته انجام بشه از برق بکشید، خود نخ در جاهایی که مناسبه متوقف بشه چک می کنه که درخواست توقف داده اید یا نه. شما با ManualResetEvent فقط نخ رو در جاهایی متوقف می کنید که وضعیتش در کد مناسب اینکار ئه. در کد کاملا روی این توقف ها کنترل دارید. اما در Suspend هیچ درکی از اینکه کجا کد رو متوقف کرده اید ندارید، چشم بسته میگید وایسا.

ممنون استاد علی
اینو توی صفحه ی 12 گفتین
میشه بجای استفاده از کلاس ManualResetEvent ، از همون اعضای کلاس Thread برای مکث و ادامه و استوپ دادن کدها استفاده کرد دیگه؟ درسته؟
فرق استفاده از این دو کلاس برای مکث و .. .چیه مگه که شما ازش استفاده کردین؟ انگار سرعت اجرای کد با این کلاس ، کمتر هم هست!
اصولا خیر، نمیشه. دلیلش اینه که شما همینطوری بهش میگید وایسا. کجا وایسه رو نه متوجه میشید و نه رویش کنترلی دارید که بگید فلان نقطه از کد رسیدی وایسا. اینکه متوجه نمیشید که Thread رو در چه موقعیتی ای رو متوقف می کنید ایراد بزرگیه، کلا در Thread ها داره یک کاری انجام میشه که به نوعی با سایر بخش ها مرتبط ئه. خیلی به ندرت کاری در Thread انجام میشه که هیچ ربطی به سایر Thread ها نداشته باشه. این ارتباط لازمش هماهنگی نخ هاست که حداقل در یکی دو بخش کد نخ لازمه. وقتی ندونید کجا دارید کد رو متوقف می کنید ممکنه در بخش نامناسبی متوقف بشه که اصل هماهنگی نخ ها رو بهم بزنه. احتمالا از اونجایی که هنوز کار خاصی با این چند نخی انجام نداده اید متوجه منظورم یا اهمیت این هماهنگی نشید ولی زمانی که واقعا از کد نویسی چند نخی بصورت موثر استفاده کنید درگیرش خواهید شد.
در ضمن هر متدی که obsolete یا deprecate ئه باید بذارید کنار و ازشون استفاده نکنید. هیچ تضمینی وجود نداره که درست کار کنند یا در نسخه های بعدی وجود داشته باشند. منظورم Resume و Suspend اینا است، بگذاریدشون کنار و ازشون استفاده نکنید، فرض کنید وجود ندارند.

سرعتش بستگی به نرخ چک کردن ها بستگی داره. انگار که شما در ثانیه صد هزار بار چک کنید که اگر کلید ESC فشار داده شد کد متوقف بشه. بجاش می توانید یک ثانیه کد رو بی وقفه اجرا کنید و بعد یکبار چک کنید که آیا ESC فشار داده شده و اگر نشد باز یک ثانیه اجرای بی وقفه داشته باشید. دلیل کاهش سرعت الان همینه که در حلقه تعداد چک کردن ها بالاتر از حد مورد نیاز ئه.
 

the_king

مدیرکل انجمن
ممنون استاد علی
استاد علی ، اینو توی صفحه ی 12 گفتین
انگار کلاس هایی که seald نباشن ، توی محدودیت ایجاد کردن در Generic ها مشکلی ندارن مثل کلاس button یا treenode و ... :

کد:
        public type1 Generic<type1>(type1 par1) where type1 : Button, new()
        {
            return new type1();
        }

ولی طبق گفته تون ، برای string ها ، اینترفیس هایی که به ارث برد رو گذاشتم :


کد:
        public type1 Generic<type1>(type1 par1) where type1 : IComparable, ICloneable, IConvertible, IEnumerable, IComparable<String>, IEnumerable<char>, IEquatable<String>, new()
        {
            return new type1();
        }

تعریف متد ، مشکلی نداره ولی موقع فراخونی ، اشکال زیر رو میگیره :


کد:
Severity    Code    Description    Project    File    Line    Suppression State
Error    CS0310    'string' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'type1' in the generic type or method 'Page2.Generic<type1>(type1)'    Practice 1    E:\Project\Visual Studio\C#.Net\Saved Project\Practice 1\Practice 1\MyUserControl\For Main Form\Page2.cs    409    Active

چرا؟

چون شما به کامپایلر گفته اید که اون type1 یک سازنده بدون پارامتر ()new داره و حالا کامپایلر داره به شما میگه string فاقد متد سازنده بدون پارامتر ئه و نمیشه با کدی مثل ()var s = new string ایجادش کرد.


بعد اینکه وقتی میخوایم محدودیت بذاریم ، فقط برای یک کلاس اما از چندین اینترفیس میشه برای محدودیت استفاده کرد دیگه؟ (مثل ارث بری) . درسته؟ یعنی نمیشه همزمان برای 2 کلاس (مثلا Button و TreeNode با هم) محدودیت ایجاد کرد. درسته؟
اجازه نمیده چون حتی اگه همچین محدودیتی هم میشد ایجاد کرد قابل استفاده نبود.
کلا در زبان #C دلیل اینکه interface رو اضافه کردند همینه که یک کلاس صرفا میتونه وارث یک کلاس باشه و وراثت چندگانه در #C وجود نداره. interface رو برای پوشش دادن این مساله اضافه کردن تا کلاس بتونه چندین interface رو پیاده سازی کنه.

مشکل اینکه نمیذاره از کلاس های seald شده در محدودیت استفاده کنیم ، چیه؟

generic type قراره اجازه تنوع type بده، یعنی حق انتخاب داشته باشید. اگه اجازه بده از کلاس sealed استفاده کنید مثل اینه که بگید type1 هر نوعی میتونه باشه به شرطی که فقط string باشه، اینجا حق انتخابی وجود نداره، وقتی string وارثی نمیتونه داشته باشه و type ها محدود شده به صرفا یک گزینه مشخص string، و بجز اون هیچ مورد دیگه ای نمیتونه انتخاب بشه دیگه generic type معنی نداره.


منظورتون از جمله ی :
"نمیخواد بگه myType چه مقادیری میتونه باشه، میخواد مشخص کنه که از نظر وراثت اش چه ویژگی ای داره"
و دقیقا کلمه ی "ویژگی ای" ، چیه؟
مواردی که باید باهاشون سازگار باشه، is یا Contains در موردشون صدق کنه. مثلا اینکه is Control باشه یا ()Contains new باشه.


تازه تست کردم ! struct ها چرا بصورت پیش فرض و توی خودشون sealed ان؟!
وراثت از یه Value Type با توجه به ساختارش عملی نیست. Value Type ها صرفا یک فضای حافظه دارند، ارجاعی در کار نیست. قراره همینطور کلی از یک متغیر مقادیر کپی بشن در یک متغیر دیگه. اگه قراره Point باشه، صرفا باید حافظه اش اندازه یک Point باشه و دقیقا مطابق با ساختار یک Point. بدون کوچکترین تغییر. اگه در وارثش ساختار یک تغییر کوچیک بکنه دیگه نمیشه مثل Value Type ها کپی بشه. وراثت ساختار حافظه شو تغییر میده. اگه بخواهید مثلا کلاس ها که در وراثت انواع تغییرات و override و new کردن و ... دارند باهاشون رفتار کنید دیگه نمیشه با این شیوه مقدارشون رو کپی کرد.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
اساسا نخ باید در جاهای مشخصی متوقف بشه، نه هر جایی. و شما در نخ کنترل کننده هم باید بدونید و بتونید کنترل کنید که کجا وایسه. معمولا در نخ ها یکسری منابع مشترک هست، یکسری هماهنگی بین نخ ها هست، مثل اینه که ماشینی رو وسط اتوبان یهو suspend کنید، یا یک منبعی مشترکی رو که قراره یک لحظه بدید دست یک نخ رو بدید و هنوز آزادش نکرده suspend اش کنید. اینها همه شون مساله است و ایراد ئه. ویژگی ManualResetEvent اینه که بجای اینکه یهو پریز نخ رو وسط یک کار مهم که باید تا ته انجام بشه از برق بکشید، خود نخ در جاهایی که مناسبه متوقف بشه چک می کنه که درخواست توقف داده اید یا نه. شما با ManualResetEvent فقط نخ رو در جاهایی متوقف می کنید که وضعیتش در کد مناسب اینکار ئه. در کد کاملا روی این توقف ها کنترل دارید. اما در Suspend هیچ درکی از اینکه کجا کد رو متوقف کرده اید ندارید، چشم بسته میگید وایسا.


اصولا خیر، نمیشه. دلیلش اینه که شما همینطوری بهش میگید وایسا. کجا وایسه رو نه متوجه میشید و نه رویش کنترلی دارید که بگید فلان نقطه از کد رسیدی وایسا. اینکه متوجه نمیشید که Thread رو در چه موقعیتی ای رو متوقف می کنید ایراد بزرگیه، کلا در Thread ها داره یک کاری انجام میشه که به نوعی با سایر بخش ها مرتبط ئه. خیلی به ندرت کاری در Thread انجام میشه که هیچ ربطی به سایر Thread ها نداشته باشه. این ارتباط لازمش هماهنگی نخ هاست که حداقل در یکی دو بخش کد نخ لازمه. وقتی ندونید کجا دارید کد رو متوقف می کنید ممکنه در بخش نامناسبی متوقف بشه که اصل هماهنگی نخ ها رو بهم بزنه. احتمالا از اونجایی که هنوز کار خاصی با این چند نخی انجام نداده اید متوجه منظورم یا اهمیت این هماهنگی نشید ولی زمانی که واقعا از کد نویسی چند نخی بصورت موثر استفاده کنید درگیرش خواهید شد.
در ضمن هر متدی که obsolete یا deprecate ئه باید بذارید کنار و ازشون استفاده نکنید. هیچ تضمینی وجود نداره که درست کار کنند یا در نسخه های بعدی وجود داشته باشند. منظورم Resume و Suspend اینا است، بگذاریدشون کنار و ازشون استفاده نکنید، فرض کنید وجود ندارند.

سرعتش بستگی به نرخ چک کردن ها بستگی داره. انگار که شما در ثانیه صد هزار بار چک کنید که اگر کلید ESC فشار داده شد کد متوقف بشه. بجاش می توانید یک ثانیه کد رو بی وقفه اجرا کنید و بعد یکبار چک کنید که آیا ESC فشار داده شده و اگر نشد باز یک ثانیه اجرای بی وقفه داشته باشید. دلیل کاهش سرعت الان همینه که در حلقه تعداد چک کردن ها بالاتر از حد مورد نیاز ئه.

ممنون استاد علی:rose:
در چند جا که در پستی که دادید ، هایلایت کردم ، درباره ی این صحبت کردید که وقتی با Thread.Suspend و ... استفاده کنیم ، نمیدونیم کد در کجا متوقف میشه . این یعنی چی؟
خوب کد در همون خطی که این تابع رو نوشتیم ، متوقف میشه و بعدا با متد Thread.Resume از همونجا به بعد ادامه پیدا میکنه دیگه
غیر از اینه مگه؟


چون شما به کامپایلر گفته اید که اون type1 یک سازنده بدون پارامتر ()new داره و حالا کامپایلر داره به شما میگه string فاقد متد سازنده بدون پارامتر ئه و نمیشه با کدی مثل ()var s = new string ایجادش کرد.
یک مثالی میزنید که چجوزی هه؟
من کد زیر رو نوشتم (چون یک اورلود متد سازنده ی string ها ، آرایه ای از کاراکترها رو میگرفت) ولی بازم این بار حتی موقع تعریف متد ، مشکل داره :


کد:
        public type1 Generic<type1>(type1 par1) where type1 : IComparable, ICloneable, IConvertible, IEnumerable, IComparable<String>, IEnumerable<char>, IEquatable<String>, new(char[] ab)
        {
            return new type1();
        }

مواردی که باید باهاشون سازگار باشه، is یا Contains در موردشون صدق کنه. مثلا اینکه is Control باشه یا ()Contains new باشه.

من دقیق متوجه نشدم
:)

وراثت از یه Value Type با توجه به ساختارش عملی نیست. Value Type ها صرفا یک فضای حافظه دارند، ارجاعی در کار نیست. قراره همینطور کلی از یک متغیر مقادیر کپی بشن در یک متغیر دیگه. اگه قراره Point باشه، صرفا باید حافظه اش اندازه یک Point باشه و دقیقا مطابق با ساختار یک Point. بدون کوچکترین تغییر. اگه در وارثش ساختار یک تغییر کوچیک بکنه دیگه نمیشه مثل Value Type ها کپی بشه. وراثت ساختار حافظه شو تغییر میده. اگه بخواهید مثلا کلاس ها که در وراثت انواع تغییرات و override و new کردن و ... دارند باهاشون رفتار کنید دیگه نمیشه با این شیوه مقدارشون رو کپی کرد.
ممنون
هر چند شاید جای بحث داشته باشه
 

the_king

مدیرکل انجمن
ممنون استاد علی:rose:
در چند جا که در پستی که دادید ، هایلایت کردم ، درباره ی این صحبت کردید که وقتی با Thread.Suspend و ... استفاده کنیم ، نمیدونیم کد در کجا متوقف میشه . این یعنی چی؟
خوب کد در همون خطی که این تابع رو نوشتیم ، متوقف میشه و بعدا با متد Thread.Resume از همونجا به بعد ادامه پیدا میکنه دیگه
غیر از اینه مگه؟
یک نخ دارید که داره تو اتوبان با سرعت میره، شما از یک نخ دیگه بهش میگید توقف کن، اون نخ که نمیدونه این وسط اتوبانه و نباید همونجا متوقف بشه، نخ خودرو هم که نمی دونه دارن متوقفش می کنن که سرعت رو کم کنه و راهنما بزنه و یواش یواش بره سمت حاشیه جاده و بعد متوقف بشه، همونجا در جا وسط اتوبان بدون اینکه کنترلی رو قضیه داشته باشه متوقف میشه.
شما وقتی توی نخ تون منبع مشترک دارید باید انحصار متقابل ایجاد کنید تا با نخ های دیگه تداخل پیش نیاد، مثلا lock اش کنید، حالا وسط lock تون متوقف بشه چی میشه؟ نه خودش ازش استفاده می کنه و نه آزادش می کنه که بقیه استفادش کنن. خودش میره تو کما، اون یکی نخ هایی که میخوان lock کنند هم منتظر میمونن و میرن تو کما.

یک مثالی میزنید که چجوزی هه؟
چی چجوریه؟

من کد زیر رو نوشتم (چون یک اورلود متد سازنده ی string ها ، آرایه ای از کاراکترها رو میگرفت) ولی بازم این بار حتی موقع تعریف متد ، مشکل داره :

کد:
        public type1 Generic<type1>(type1 par1) where type1 : IComparable, ICloneable, IConvertible, IEnumerable, IComparable<String>, IEnumerable<char>, IEquatable<String>, new(char[] ab)
        {
            return new type1();
        }
مشکل رو که حل نکردید، اون (new(char[] ab ئه درست ولی با دقت به کدتون نگاه کنین، نوشتید ()return new type1 و باید بتونید بجای type1 ئه string قرار بدید. کامپایلر تا وقتی نوع مشخصی بهش ندید نمیتونه قضاوتی بکنه چون شاید اون نوع خاص سازنده بدون پارامتر رو پشتیبانی کنه، در حال کامپایل نمیتونه نتیجه بگیره کدتون اشتباهه. اما حالا شما توی یک کد مستقل بنویسید ()var s = new string و ببینید عملی هست یا خیر. نیست پس همین Generic شما هم عملی نیست و از خطاش نباید تعجب کنید.
 

SajjadKhati

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




چی چجوریه؟

مشکل رو که حل نکردید، اون (new(char[] ab ئه درست ولی با دقت به کدتون نگاه کنین، نوشتید ()return new type1 و باید بتونید بجای type1 ئه string قرار بدید. کامپایلر تا وقتی نوع مشخصی بهش ندید نمیتونه قضاوتی بکنه چون شاید اون نوع خاص سازنده بدون پارامتر رو پشتیبانی کنه، در حال کامپایل نمیتونه نتیجه بگیره کدتون اشتباهه. اما حالا شما توی یک کد مستقل بنویسید ()var s = new string و ببینید عملی هست یا خیر. نیست پس همین Generic شما هم عملی نیست و از خطاش نباید تعجب کنید.

همین کدی که دادم ، چجوری هه قضیه اش؟ یعنی باید چجوری تغییر بدم تا ارور نده؟
قسمت return رو همین طور که گفتین تغییر دادم ولی بازم ارور میده!
 

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

بالا