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

SajjadKhati

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

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

توی سی شارپ ، اگه آرایه در آرایه استفاده کنیم ، نمیشه یه آرایه ی داخلی رو بصورت اینیتیالایز new کرد ولی یکی دیگه رو بعدا؟ یعنی این جوری نمیشه . درست میگم؟ :


کد:
public partial class Form1 : Form
{
Control[][] pagesControls = new Control[2][];
        public Form1()
        {
            pagesControls = new Control[][] { new Control[] { contGroupBoxAddCircle,    contGroupBoxTest1, contGroupBoxTreeView, txtboxGeneralUsage } };                 pagesControls[1] = new Control[2];
         }
   }

در کد بالا ، خط آخر رو اشکال میگیره
یا باید هر دو عضو رو بصورت اینیتیالایز new کرد یا اینکه هر دو عضو رو بعدا با استفاده از ایندکس و اندیس new کرد . درسته؟
اگه اون راه اول شدنی هه ، کسی میگه چجوری و چرا کد بالا خطا میده؟


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

استاد علی ، پست ها توی این انجمن ، قابلیت ویرایش نداره که هر بار پست جدید نذارم؟ قبلا این قابلیت رو داشت !

ممنون
 

the_king

مدیرکل انجمن
سلام
ممنون استاد علی :rose:
ولی من خوب متوجه نشدم . یعنی متوجه شدم ولی سئوال منو فکر کنم درست نخوندین . منظورم اینه که در اون کدی که دادم ، یه چیز (شی) دینامیک روبدون تضمین ریختیم توی شی غیر دینامیک ولی توی کد دوم فقط در صورت تضمین و تبدیل ، تونستیم یه شی دینامیک رو توی غیر دینامیک بریزیم . توی هر دو ، شی دینامیک ریخته شدن توی غیر دینامیک اما یکی با تضمین و تبدیل و یکی دیگه بدون تضمین و تبدیل !!!

من پیاده سازی کد FindPatterns تون رو ندیدم ولی قطعا حالتی داره که این خطای کامپایل رو سبب شده، چنانکه همچین کدی این خطا رو ایجاد نمی کنه :
کد:
        private dynamic FindPatterns(string text1, string text2)
        {
            return "test";
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            string[,] findedText = this.FindPatterns("85 salam 435.25", "\\d");
        }
و حتما این نکته رو در نظر بگیرید که این تبدیل بدون خطای کامپایل به این معنی نیست که خطایی در زمان اجرا اتفاق نمیافته :
کد:
            try
            {
                dynamic a = "salam";
                int h = a;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

کد:
        private dynamic FindPatterns(string text1, string text2)
        {
            return "test";
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            try
            {
                string[,] findedText = this.FindPatterns("85 salam 435.25", "\\d");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

یه سئوال اینکه چرا وقتی کلاسی رو Generic تعریف میکنم که اون کلاس از کلاسی ارث برد ، همه ی کنترل (اشیاء) هایی که تعریف کردم ، از دسترس خارج میشه؟ یعنی وقتی کد رو بصورت زیر تعریف میکنم ، هر کنترل و شی ای که بصورت دستی توی Form1 گذاشتم (دکمه و لیبل و ...) ، شی اش دیگه در دسترس ام نیست و کمپایلر ارور میده که اشیاء در دسترس نیست . چرا؟ :


کد:
public partial class Form1<T> : Form
شما آگاهانه از اون <T> استفاده می کنید؟ در نظر گرفتید که برای شیء ساختن از Form1 باید نوع <T> رو هم بنویسید تا شیء ای با اون نوع خاص <T> ساخته بشه؟ مثلا :
کد:
Application.Run(new Form1<int>())
بجای :
کد:
            Application.Run(new Form1());

و در نظر بگیرید که برای اینکه یکی دو متد Generic در کلاسی داشته باشید لزومی نداره که کل کلاس رو Generic کنید :
کد:
        public static void Test<T>(T value)
        {
            MessageBox.Show(value.ToString());
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Test<int>(12);
        }
مثلا Array رو در نظر بگیرید، مملو از انواع متد های Generic ئه، ولی برای این منظور خودش Generic نوشته نشده. البته اون متد ها static اند و کلاس کسی برای استفاده از اون متد ها شیء از Array نمیسازه.

بعد اینکه ، شما با Regex ها کار کردین ، سئوالات مو درباره اش بپرسم؟
ممنون
بله، قبلا هم در مورد Regex صحبت شده. لینک
برای ساختن regex می توانید از سایت هایی مثل regexr.com هم استفاده کنید.
بعد اینکه ، توی قضیه ی جنریک ها ، چرا نمیتونم محدودیت نوع string و ... بذارم (فقط باید نوع کلی مثل class و struct و... رو مشخص کنم) ؟
متوجه سوال تون نمیشم، بهتره یک نمونه کدی که فکر می کنید باید می توانستید بنویسید و به نظر خودتون باید مورد قبول کامپایلر می بود رو مثال بزنید.
 

the_king

مدیرکل انجمن
یه سئوال اینکه ، ما چرا اولین فرمی که میخوایم نمایش بدیم ، از متد Application.Run استفاده میکنیم یعنی موقع اولین نمایش یک فرم (مثلا نمایش فرم1 که بصورت پیش فرض ، اولین فرم اجرایی هست) ، نمیشه شی ای از اون فرم درست و از متد Show خود همون فرم استفاده کنیم اما وقتی در یک فرم جاری هستیم ، میتونیم از متد Show (از شی یک فرم) ، اون فرم رو نمایش بدیم؟
یعنی اولین بار ، مشکلش چیه؟ (اگه اولین بار این کار رو کنیم ، فرم یه لحظه اجرا میشه و فوری بسته میشه)
ممنون
چرا، اگه فرم رو با ShowDialog نشون بدید فرم فورا بسته نمیشه، اون بسته شدن به این دلیله که اتمام اجرای اون متد Main ای که در Program.cs می بینید به معنای خاتمه اجرای برنامه است. چون باز شدن فرم modeless در اون متد Show مکث نمیکنه و کد های بعدی Main اجرا میشه، دیگه تا بخواهید روی فرم کاری انجام بدید Main به پایان رسیده.
کد:
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            using (var f1 = new Form1())
            {
                f1.ShowDialog();
            }
        }
اما Application.Run دقیقا ShowDialog نیست، تمامی رخداد های اصلی برنامه بصورت Message به پنجره اصلی برنامه ارسال میشه، وقتی شما با ShowDialog کار می کنید این پیغام های خاص پنجره اصلی برنامه مدیریت نمیشن. مثلا برای رخداد بسته شدن فرم آمادگی داره تا بسته شدن Thread برنامه رو هم مدیریت کنه. معمولا برنامه نویس هم کدی برای مدیریت دستی شون نمی نویسه پس توصیه میشه که برای فرم اصلی برنامه از Application.Run استفاده کنید، حتی اگه به نظرتون ShowDialog کافی باشه.


توی سی شارپ ، اگه آرایه در آرایه استفاده کنیم ، نمیشه یه آرایه ی داخلی رو بصورت اینیتیالایز new کرد ولی یکی دیگه رو بعدا؟ یعنی این جوری نمیشه . درست میگم؟ :


کد:
public partial class Form1 : Form
{
Control[][] pagesControls = new Control[2][];
        public Form1()
        {
            pagesControls = new Control[][] { new Control[] { contGroupBoxAddCircle,    contGroupBoxTest1, contGroupBoxTreeView, txtboxGeneralUsage } };                 pagesControls[1] = new Control[2];
         }
   }

در کد بالا ، خط آخر رو اشکال میگیره
یا باید هر دو عضو رو بصورت اینیتیالایز new کرد یا اینکه هر دو عضو رو بعدا با استفاده از ایندکس و اندیس new کرد . درسته؟
اگه اون راه اول شدنی هه ، کسی میگه چجوری و چرا کد بالا خطا میده؟

می توانید هر کدوم رو جدا بنویسید :
کد:
            Control[][] pagesControls = new Control[2][];
            pagesControls[0] = new Control[] { contGroupBoxAddCircle, contGroupBoxTest1, contGroupBoxTreeView, txtboxGeneralUsage };
            .
            .
            .
            pagesControls[1] = new Control[] { contGroupBoxAddCircle, contGroupBoxTest1, contGroupBoxTreeView, txtboxGeneralUsage };
یا هر دو رو یکجا :
کد:
            Control[][] pagesControls = new Control[2][]
            {
                new Control[] { contGroupBoxAddCircle, contGroupBoxTest1, contGroupBoxTreeView, txtboxGeneralUsage } ,
                new Control[] { contGroupBoxAddCircle, contGroupBoxTest1, contGroupBoxTreeView, txtboxGeneralUsage }
            };
استاد علی ، پست ها توی این انجمن ، قابلیت ویرایش نداره که هر بار پست جدید نذارم؟ قبلا این قابلیت رو داشت !
Untitled-1.gif
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
من پیاده سازی کد FindPatterns تون رو ندیدم ولی قطعا حالتی داره که این خطای کامپایل رو سبب شده، چنانکه همچین کدی این خطا رو ایجاد نمی کنه :
کد:
        private dynamic FindPatterns(string text1, string text2)
        {
            return "test";
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            string[,] findedText = this.FindPatterns("85 salam 435.25", "\\d");
        }

سلام استاد علی
ممنون از جواب تون :rose:

پیاده سازی این متد ، این جوری هه :

کد:
public dynamic[,] FindPatterns(string text4Search, string patterns)
        {
            dynamic[,] resualt = new dynamic[0, 3];
            Regex regexPattern = new Regex(patterns);
            Match finded;
            int index = 0;

            while (true)
            {
                if (index >= text4Search.Length)
                    break;

                finded = regexPattern.Match(text4Search, index);
                if (finded.Success)
                {
                    resualt = myFrm.AddToDynamicArray<dynamic, string>(resualt, finded.Index.ToString(), finded.Length.ToString(), finded.Value);
                    index = finded.Index + finded.Length;
                }
                else
                    break;
            }

            if (index > -1)
                return resualt;
            else
                return null;
        }
پیاده سازی AddToDynamicArray هم این طوری هه :

کد:
public dynamic[,] AddToDynamicArray<myType, valueType>(myType[,] entryArray,params valueType[] data) where myType :class
        {
            int zeroArrayLength = entryArray.GetLength(0);
            dynamic[,] copyArray = new dynamic[zeroArrayLength + 1, 3];

            for (int i = 0; i < copyArray.GetLength(0); i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    if (i + 1 == copyArray.GetLength(0))
                        copyArray[i, j] = data[j];
                    else
                        copyArray[i, j] = entryArray[i, j];
                }
            }
           
            return copyArray;
        }


و حتما این نکته رو در نظر بگیرید که این تبدیل بدون خطای کامپایل به این معنی نیست که خطایی در زمان اجرا اتفاق نمیافته :
کد:
            try
            {
                dynamic a = "salam";
                int h = a;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

کد:
        private dynamic FindPatterns(string text1, string text2)
        {
            return "test";
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            try
            {
                string[,] findedText = this.FindPatterns("85 salam 435.25", "\\d");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

آره میدونم . منظورم ، زمان کمپایل (نوشتن کد) هست که ارور نده


شما آگاهانه از اون <T> استفاده می کنید؟ در نظر گرفتید که برای شیء ساختن از Form1 باید نوع <T> رو هم بنویسید تا شیء ای با اون نوع خاص <T> ساخته بشه؟ مثلا :
کد:
Application.Run(new Form1<int>())
بجای :
کد:
            Application.Run(new Form1());

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


و در نظر بگیرید که برای اینکه یکی دو متد Generic در کلاسی داشته باشید لزومی نداره که کل کلاس رو Generic کنید :
کد:
        public static void Test<T>(T value)
        {
            MessageBox.Show(value.ToString());
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Test<int>(12);
        }
مثلا Array رو در نظر بگیرید، مملو از انواع متد های Generic ئه، ولی برای این منظور خودش Generic نوشته نشده. البته اون متد ها static اند و کلاس کسی برای استفاده از اون متد ها شیء از Array نمیسازه.

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


بله، قبلا هم در مورد Regex صحبت شده. لینک
برای ساختن regex می توانید از سایت هایی مثل regexr.com هم استفاده کنید.

آره . اتفاقا از همون راهنمایی هاتون بود که متوجه قضیه ی مبحث رجیکس بجای پترن در سی شارپ شدم
یه رشته دارم که میخوام پترنی بنویسم که هر چی عدد اومد و بعدش نقطه اومد ؛ به شرطی که آخرش عدد باشه رو بگیره منتها میخوام اگه تک عدد هم اومد بگیره . پترنم اینه :

کد:
string dateSearch = "+17.492.4.03..81  -28  74 871.  ^_ __  __Array25.M*aduleVersion541.....579*( )( Arr1) ^245_54/x-1+Ywq*+365Int      +sp*ace^_\n\a";
            regexPattern = new Regex("(\\d+\\.?){1,}\\d+");  /
            MatchCollection resault = regexPattern.Matches(dateSearch);
            foreach (Match item in resault)
                MessageBox.Show("index :  " + item.Index + "\nlength :  " + item.Length + "\n\n\n" + item.Value);
الگوری قسمت اول یعنی عدد و بعد نقطه و آخر آخرش عدد باشه رو درست انجام میده ولی نمیدونم چجوری تغییرش بدم که تک رقم رو قبول کنه . این پترن ، در رشته ی بالا ، عدد یک که تک رقم هست رو نمیتونه پیدا و مچ کنه (عدد یک که قبل اش ^245_54/x- و بعد از اون هم +Ywq*+365Int اومد)
باید چجور تغییر کنه؟


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

مثلا من اگه در همون متد AddToDynamicArray که بصورت کامل در بالا کدش رو دادم ، بیام هدش رو در قسمت شرط (where) از class به int تغییر بدم ، کمپایلر ارور میده :
کد:
public dynamic[,] AddToDynamicArray<myType, valueType>(myType[,] entryArray,params valueType[] data) where myType :string
اگه به string تغییر بدم ، چون کلاس seaed هست اشکال میگیره
نوع int رو بخاطر استراکچر بودنش اشکال میگیره؟



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

متصل شدن نیست، اصلا به این معنا نیست که برنامه معطل بشو تا این پنجره MessageBox بسته بشه. شما فقط مشخص می کنید که این پنجره از طرف پنجره Task Manager باز شده. این یک راهنمایی برای ویندوز ئه که بدونه کدوم پنجره درخواست باز شدن این MessageBox رو کرده.
اون پنجره Modal که شما برای MessageBox مشخص می کنید ربطی به منتظر بودن برنامه نداره، اینکه برنامه خودتون موقع باز شدن یک پنجره Modal منتظر می مونه بخاطر اینه که دستورات در یک نخ Thread اجرا میشه و تا MessageBox کدی رو برنگردونه دستورات بعدی اجرا نمیشه. اون پارامتر پنجره صرفا به این جهت ئه که ویندوز بدونه این MessageBox از طرف کدوم پنجره دیگه باز شده و فرضا وقتی بسته شد Focus به کجا برگرده.
از اونجایی که شما هیچ تسلط ای رو کد برنامه Task Manager ندارید نمی توانید مستقیما اجرای کد های اون رو معطل یک پنجره MessageBox خارجی بکنید، اینکه Modal اش اون پنجره Task Manager باشه یا نباشه ربطی به این قضیه نداره. شما برای Task Manager پنجره MessageBox باز نمی کنید، برای برنامه خودتون باز می کنید.

قبلا در صفحه ی 9 ، این توضیح رو داده بودین . کد نویسی همچین سئوالی که درباره ی چسبیدن یه پنجره ای (مثلا MessageBox.Show) به پنجره ی دیگه (مثلا Task Manager) خیلی خیلی سخته؟ اگه در حد چند خط هست ، کد یا لینک میدین ، تجزیه و تحلیل کنم؟

باز هم ممنون
:rose:
 

the_king

مدیرکل انجمن
سلام استاد علی
ممنون از جواب تون :rose:

پیاده سازی این متد ، این جوری هه :

کد:
public dynamic[,] FindPatterns(string text4Search, string patterns)
        {
            dynamic[,] resualt = new dynamic[0, 3];
            Regex regexPattern = new Regex(patterns);
            Match finded;
            int index = 0;

            while (true)
            {
                if (index >= text4Search.Length)
                    break;

                finded = regexPattern.Match(text4Search, index);
                if (finded.Success)
                {
                    resualt = myFrm.AddToDynamicArray<dynamic, string>(resualt, finded.Index.ToString(), finded.Length.ToString(), finded.Value);
                    index = finded.Index + finded.Length;
                }
                else
                    break;
            }

            if (index > -1)
                return resualt;
            else
                return null;
        }
پیاده سازی AddToDynamicArray هم این طوری هه :

کد:
public dynamic[,] AddToDynamicArray<myType, valueType>(myType[,] entryArray,params valueType[] data) where myType :class
        {
            int zeroArrayLength = entryArray.GetLength(0);
            dynamic[,] copyArray = new dynamic[zeroArrayLength + 1, 3];

            for (int i = 0; i < copyArray.GetLength(0); i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    if (i + 1 == copyArray.GetLength(0))
                        copyArray[i, j] = data[j];
                    else
                        copyArray[i, j] = entryArray[i, j];
                }
            }
       
            return copyArray;
        }

اشکال از همون تعریف FindPatterns ئه، شما می توانید از کامپایلر توقع داشته باشید که قبول کنه dynamic هر نوع داده ای می تونه باشه، ولی [,]dynamic به این معنا نیست که همه اعضاء آرایه دو بعدی از یک نوع داده خاص و یکسان اند. امتحان کنید :
کد:
            dynamic[] x = new dynamic[4];
            x[0] = "test";
            x[1] = new Control();
            x[2] = 14.5;
            x[3] = new int[] { 1, 2, 3 };

شما باید همچین تعریفی رو بکار ببرید :
کد:
        public dynamic FindPatterns(string text4Search, string patterns)

آره . اتفاقا از همون راهنمایی هاتون بود که متوجه قضیه ی مبحث رجیکس بجای پترن در سی شارپ شدم
یه رشته دارم که میخوام پترنی بنویسم که هر چی عدد اومد و بعدش نقطه اومد ؛ به شرطی که آخرش عدد باشه رو بگیره منتها میخوام اگه تک عدد هم اومد بگیره . پترنم اینه :
کد:
string dateSearch = "+17.492.4.03..81  -28  74 871.  ^_ __  __Array25.M*aduleVersion541.....579*( )( Arr1) ^245_54/x-1+Ywq*+365Int      +sp*ace^_\n\a";
            regexPattern = new Regex("(\\d+\\.?){1,}\\d+");  /
            MatchCollection resault = regexPattern.Matches(dateSearch);
            foreach (Match item in resault)
                MessageBox.Show("index :  " + item.Index + "\nlength :  " + item.Length + "\n\n\n" + item.Value);
الگوری قسمت اول یعنی عدد و بعد نقطه و آخر آخرش عدد باشه رو درست انجام میده ولی نمیدونم چجوری تغییرش بدم که تک رقم رو قبول کنه . این پترن ، در رشته ی بالا ، عدد یک که تک رقم هست رو نمیتونه پیدا و مچ کنه (عدد یک که قبل اش ^245_54/x- و بعد از اون هم +Ywq*+365Int اومد)
باید چجور تغییر کنه؟
ظاهرا کاری با - و + ندارید :
کد:
            var regexPattern = new Regex("(\\d+\\.?)*\\d+");
مثلا من اگه در همون متد AddToDynamicArray که بصورت کامل در بالا کدش رو دادم ، بیام هدش رو در قسمت شرط (where) از class به int تغییر بدم ، کمپایلر ارور میده :
کد:
public dynamic[,] AddToDynamicArray<myType, valueType>(myType[,] entryArray,params valueType[] data) where myType :string
اگه به string تغییر بدم ، چون کلاس seaed هست اشکال میگیره
اصلا where myType :string معنی نداره، myType اگر قرار باشه یک نوع داده ثابت باشه که دیگه Generic نیست و با <myType> نوشته نمیشه، بجای myType صریحا string رو می نویسند. شما می توانید بگید myType با string سازگار باشه، روی string کلید F12 رو فشار بدید، همه وراثت هایش اعم از IComparable و ICloneable و.. رو که بنویسید با string سازگار میشه. اما نمی توانید بگویید Generic باشه ولی فقط string باشه.
قبلا در صفحه ی 9 ، این توضیح رو داده بودین . کد نویسی همچین سئوالی که درباره ی چسبیدن یه پنجره ای (مثلا MessageBox.Show) به پنجره ی دیگه (مثلا Task Manager) خیلی خیلی سخته؟ اگه در حد چند خط هست ، کد یا لینک میدین ، تجزیه و تحلیل کنم؟
باز هم ممنون
:rose:
پاسخ ام رو درست متوجه نشدید، اینکه گفتم شما هیچ تسلط ای رو کد برنامه Task Manager ندارید به این معنا نیست که معلومات شما کافی نیست، شما که کد برنامه Task Manager رو که ندارید که بخواهید تغییرش بدهید و یک پنجره رو داخلش نمایش بدید. چسبیدن یک پنجره به یک برنامه دیگه تعریفی است از کاری که شما می خواهید می کنید، نمونه عملی در برنامه نویسی ویندوز نداره. شما کجا نمونه همچین چیزی رو دیدید که یک پنجره از برنامه شما بچسبه به یک برنامه دیگه که الان بخواهید مشابه اش رو بسازید.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
کد:
            var regexPattern = new Regex("(\\d+\\.?)*\\d+");

ممنون
تازه فهمیدم . یا این طور هم میشه نوشت :


کد:
regexPattern = new Regex("(\\d+\\.?){0,}\\d+");

اصلا where myType :string معنی نداره، myType اگر قرار باشه یک نوع داده ثابت باشه که دیگه Generic نیست و با <myType> نوشته نمیشه، بجای myType صریحا string رو می نویسند. شما می توانید بگید myType با string سازگار باشه، روی string کلید F12 رو فشار بدید، همه وراثت هایش اعم از IComparable و ICloneable و.. رو که بنویسید با string سازگار میشه. اما نمی توانید بگویید Generic باشه ولی فقط string باشه.

نه
مثلا اگه بخوام 2 تا بدم مثل string, int و حالا هر چیز دیگه . چرا هیچ کدوم رو قبول نمیکنه؟ . کلا باید اینترفیس هایی که والدشونن رو بدم بجای کلاس خواص؟


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

حالا این بماند
کلا منظورم کار کردن با هندل کلاس های دیگه در توابع دیگه هست
پس این آرگومان های IWin32Window در پارامترهای توابع (مثل تابع MessageBox.Show) ، برای چی هستن؟
یا مثلا اگه بخوایم همین متد MessageBox.Show رو وابسته به پنجره ی برنامه و فرم خودمون نکنیم ، باید چی کار کنیم؟
ممنون

:rose:
 

the_king

مدیرکل انجمن
نه
مثلا اگه بخوام 2 تا بدم مثل string, int و حالا هر چیز دیگه . چرا هیچ کدوم رو قبول نمیکنه؟ . کلا باید اینترفیس هایی که والدشونن رو بدم بجای کلاس خواص؟
کلا نوع رابطه با اون چیزی که تصور می کنید متفاوته، where myType :string عملگر : داره نه = ، نمیخواد بگه myType چه مقادیری میتونه باشه، میخواد مشخص کنه که از نظر وراثت اش چه ویژگی ای داره. string که sealed ئه و نمیشه ازش وراثتی داشت، int هم که struct ئه و struct ها جزو موارد قابل وراثت نیستند. شما هر نوع struct ای رو در هر صورت و هر کلاسی که sealed باشه نمی توانید برای : where T بکار ببرید.
حالا این بماند
کلا منظورم کار کردن با هندل کلاس های دیگه در توابع دیگه هست
پس این آرگومان های IWin32Window در پارامترهای توابع (مثل تابع MessageBox.Show) ، برای چی هستن؟
یا مثلا اگه بخوایم همین متد MessageBox.Show رو وابسته به پنجره ی برنامه و فرم خودمون نکنیم ، باید چی کار کنیم؟
ممنون
کلاس ها به اون مفهوم Handle ندارند، در نظر بگیرید که سیستم عامل مسئول ایجاد کردن شیء از کلاس ها نیست که بگید یک سیستم یکپارچه مشخص برای ایجاد کردن Handle وجود داره که شما با داشتن Handle بتوانید با هر کلاسی به فلان نحو کار کنید.
من که براتون توضیح دادم که اون پارامتر پنجره در MessageBox.Show برای چیه، فرضا برای اینه که مشخص بشه وقتی پنجره بسته شد Focus به کجا برگرده.
MessageBox یک فرم DialogBox ئه، DialogBox ها ذاتا پنجره های Modal اند، کاربرد شون طوری نیست که کسی بخواد Modeless نشون شون بده.
به همین جهت MessageBox پنجره non-Modal / Modeless نمیسازه.
شما می توانید هم خودتون یک فرم بسازید و با متد Show بصورت Modeless نشون اش بدید که اجرای کد در Thread تون معطل بسته شدنش نشه و یا می توانید MessageBox.Show رو در یک Thread مجزا نمایش بدید که باز به این طریق Thread اصلی کد تون رو معطل نمی کنه، ولی به هر حال MessageBox برای ایجاد کردن پنجره های Modeless نیست و پنجره Modal میسازه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
کلا نوع رابطه با اون چیزی که تصور می کنید متفاوته، where myType :string عملگر : داره نه = ، نمیخواد بگه myType چه مقادیری میتونه باشه، میخواد مشخص کنه که از نظر وراثت اش چه ویژگی ای داره. string که sealed ئه و نمیشه ازش وراثتی داشت، int هم که struct ئه و struct ها جزو موارد قابل وراثت نیستند. شما هر نوع struct ای رو در هر صورت و هر کلاسی که sealed باشه نمی توانید برای : where T بکار ببرید.

سلام
ممنون استاد علی :rose:
دقیق متوجه نشدم
بجای : ، مساوی ( = ) هم گذاشتم ، بازم برای هیچ کدوم نپذیرفت


و یا می توانید MessageBox.Show رو در یک Thread مجزا نمایش بدید که باز به این طریق Thread اصلی کد تون رو معطل نمی کنه، ولی به هر حال MessageBox برای ایجاد کردن پنجره های Modeless نیست و پنجره Modal میسازه.

چجوری میشه در یک Thread مجزا نمایش بدیم؟ من درباره ی Thread و این چیزا چیزی نمیدونم

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

یک سئوال دیگه اینکه ، خوب میدونیم وقتی به اعضایی از کلاسی دسترسی پیدا کنیم ، باید فراخونی شون کنیم
متد Main در کلاس Program اصلا خودش از کجا فراخونی میشه؟
اون 2 تا متد در کلاس Main یعنی :


کد:
Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

هم مربوط به ظاهر گرافیکی اپلیکیشن هه دیگه . درسته؟
 

the_king

مدیرکل انجمن
سلام
ممنون استاد علی :rose:
دقیق متوجه نشدم
بجای : ، مساوی ( = ) هم گذاشتم ، بازم برای هیچ کدوم نپذیرفت
نگفتم که : رو بردارید و بجاش = قرار بدید، Query نیست که بخواهید هر طوری بنویسید، دستور زبان اش مشخص و ثابته، اون where خاصیت اش همونه که ویژگی وراثت رو مشخص کنید، برای این نیست که بخواهید به یکی دو نوع داده ثابت محدودش کنید. اون , ها هم به معنای AND ئه، نه OR، اگر بنویسید where myType : C1, I2 باید myType هم وارث کلاس C1 باشه و هم وارث اینترفیس I2، حتی اجازه ندارید که با where myType : C1, C2 همزمان وارث دو کلاس اش کنید، در where فقط میشه یک کلاس پایه مشخص بشه.

چجوری میشه در یک Thread مجزا نمایش بدیم؟ من درباره ی Thread و این چیزا چیزی نمیدونم
یا مستقیما Thread می سازید و یا بصورت غیر مستقیم با کمپوننت BackgroundWorker کار می کنید، استفاده از BackgroundWorker رو در اولویت قرار بدید چون اگه Thread رو خوب مدیریت نکنید احتمال Crash کردن برنامه هست.
کد:
        private void button1_Click(object sender, EventArgs e)
        {
            var start=new System.Threading.ThreadStart(Test);
            var thread = new System.Threading.Thread(start);
            thread.Start();
        }

        private void Test()
        {
            MessageBox.Show("Test!");
        }

کد:
        private void button1_Click(object sender, EventArgs e)
        {
            var bw = new BackgroundWorker();
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.RunWorkerAsync();
        }

        void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            MessageBox.Show("Test!");
        }

یک سئوال دیگه اینکه ، خوب میدونیم وقتی به اعضایی از کلاسی دسترسی پیدا کنیم ، باید فراخونی شون کنیم
متد Main در کلاس Program اصلا خودش از کجا فراخونی میشه؟

در زبان هایی مثل C و ++C و ویژوال بیسیک و #C موقع اجرای برنامه تابع یا روتین هایی مثل WinMain یا main یا Main بصورت خودکار فراخوانی میشه، این جزو اصول اولیه این زبان ها است.
اون 2 تا متد در کلاس Main یعنی :

کد:
Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

هم مربوط به ظاهر گرافیکی اپلیکیشن هه دیگه . درسته؟
بله، اولی برای فعال کردن تم گرافیکی ویندوز (که اگه false باشه تم غیر فعاله و شبیه ویندوز های 98 و قبل تر میشه) و دومی برای تعیین کردن موتور نمایش متن در برنامه بین دو گزنه GDI یا Graphics Device Interface قدیمی و یا +GDI مدرن ئه و صرفا زمانی true اش می کنند که بعضی کنترل ها با +GDI کار کنند و بعضی با GDI و در نتیجه متن رو متفاوت از بقیه نشون بدن و برای یکدست شدن بخواهیم همه شون با GDI نمایش داده بشن.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
یا مستقیما Thread می سازید و یا بصورت غیر مستقیم با کمپوننت BackgroundWorker کار می کنید، استفاده از BackgroundWorker رو در اولویت قرار بدید چون اگه Thread رو خوب مدیریت نکنید احتمال Crash کردن برنامه هست.
کد:
        private void button1_Click(object sender, EventArgs e)
        {
            var start=new System.Threading.ThreadStart(Test);
            var thread = new System.Threading.Thread(start);
            thread.Start();
        }

        private void Test()
        {
            MessageBox.Show("Test!");
        }

سلام ممنون استاد علی
اینو تست کردم ولی ارور میده که نمیدونم برای چیه؟ :


jl0knwvwzeah.jpg


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

the_king

مدیرکل انجمن
سلام ممنون استاد علی
اینو تست کردم ولی ارور میده که نمیدونم برای چیه؟ :


jl0knwvwzeah.jpg
یک نگاهی به کد MessageThread1 تون بندازید، خطای دومی بخاطر خطای اولیه. MessageThread1 باید یک متد () void MessageThread1 باشه، فاقد پارامتر ورودی و فاقد مقدار بازگشتی.
این نوع کد منو یاد مبحث کوروتین ها در لوا (توقف کدها در لوا) میندازه که یه تابعی رو مینوشتیم و به عنوان ورودی به توابع کوروتین ها میدادیم . پس قضیه ی اون هم انگار همین نخ درست کردن بود
راستی ، اگه زحمت نیست ، یه مثال کوچیک درباره ی توقف کدها در سی شارپ میزنین؟ و اینکه کلا روند توقف کدها در سی شارپ چجوری هه؟
Thread رو اصولا از بیرون متوقف نمی کنند، یعنی ()thread.Suspend توصیه نمیشه، معلوم هم نیست دقیقا در چه لحظه ای متوقف اش کنه و در سر اجرای کدوم کد. Thread رو یا به سادگی مثلا با return به انتهای کد می رسونند تا تموم بشه و یا متد Abort شیء Thread رو اجرا می کنند و یا با Thread.Sleep به مدت زمانی مشخصی میخوابونند یا به انتظار یک رخدادی موقتا متوقف اش می کنند که این رخداد هم معمولا یا برای دسترسی انحصاری به یک شیء ئه که دو تا Thread همزمان سراغ یک شیء مشترک نروند و یا انتظار برای پایان انجام یک کار در Thread دیگری است که قبل از اتمامش Thread فعلی کاری برای انجام دادن نداره و بیکاره. برای موقتا متوقف کردن Thread تا بروز یک رخدادی از lock و Monitor و Mutex و AutoResetEvent و ManualResetEvent و Semaphore و ... استفاده میشه، بحث هماهنگی Thread ها بحث خیلی مفصلیه.
 

SajjadKhati

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


سلام
ممنون ازتون
آره متد MessageThread1 ام ، یه آرگومان ورودی داشت . ولی حتی با حذف این آرگومان و تغییر به کد :

کد:
public void MessageThread1()
        {
            MessageBox.Show("salam");
        }
باز هم همون ارور رو میده

Thread رو اصولا از بیرون متوقف نمی کنند، یعنی ()thread.Suspend توصیه نمیشه، معلوم هم نیست دقیقا در چه لحظه ای متوقف اش کنه و در سر اجرای کدوم کد. Thread رو یا به سادگی مثلا با return به انتهای کد می رسونند تا تموم بشه و یا متد Abort شیء Thread رو اجرا می کنند و یا با Thread.Sleep به مدت زمانی مشخصی میخوابونند یا به انتظار یک رخدادی موقتا متوقف اش می کنند که این رخداد هم معمولا یا برای دسترسی انحصاری به یک شیء ئه که دو تا Thread همزمان سراغ یک شیء مشترک نروند و یا انتظار برای پایان انجام یک کار در Thread دیگری است که قبل از اتمامش Thread فعلی کاری برای انجام دادن نداره و بیکاره. برای موقتا متوقف کردن Thread تا بروز یک رخدادی از lock و Monitor و Mutex و AutoResetEvent و ManualResetEvent و Semaphore و ... استفاده میشه، بحث هماهنگی Thread ها بحث خیلی مفصلیه.

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

the_king

مدیرکل انجمن
سلام
ممنون ازتون
آره متد MessageThread1 ام ، یه آرگومان ورودی داشت . ولی حتی با حذف این آرگومان و تغییر به کد :

کد:
public void MessageThread1()
        {
            MessageBox.Show("salam");
        }
باز هم همون ارور رو میده
یه جای کار تون اشتباهه، مثلا موقعیتی که 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 بگردید در این مورد قبلا در فروم صحبت شده.
 

SajjadKhati

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


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


سلام
ممنون استاد علی
این پروژه ی تمرینی مه . متد MessageThread1 در خط 360 و رویداد btnThread1_Click که کدهای داخلش کامنت شدن ، در خط 938 تعریف شدن (که اگه از کامنت گرفته شن ، همون ارور رو میده)
بعد اینکه چرا نباید متدهامون آرگومان داشته باشن؟


اگه بخواهید بعدا اجرا از یک موقعیت خاص ادامه پیدا کنه باید در کد داخل 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 بگردید در این مورد قبلا در فروم صحبت شده.

ممنون
این کد اینجا بمونه و وقتی که به قضیه ی دلیگیت ها (که ان شاء ا... تا هفته ی بعد بهش رسیدم) ، کامل بررسی اش کنم

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

بعد اینکه چرا عضو protected از یک کلاس ، فقط وقتی درون کلاس فرزند ، از همون کلاس فرزند شی بسازیم در دسترس هه؟ یعنی به چه دلیل وقتی از کلاس فرزند ، شی ای از کلاس پدر بسازیم ، اعضای protected تعریف شده از کلاس پدر در دسترس نیست؟

ممنون
 

the_king

مدیرکل انجمن

سلام
ممنون استاد علی
این پروژه ی تمرینی مه . متد MessageThread1 در خط 360 و رویداد btnThread1_Click که کدهای داخلش کامنت شدن ، در خط 938 تعریف شدن (که اگه از کامنت گرفته شن ، همون ارور رو میده)
تا کد رو نبینم هیچ نظری نمیتونم بدم، توضیحات تون کمکی نمی کنه.

بعد اینکه چرا نباید متدهامون آرگومان داشته باشن؟
روی ThreadStart کلید F12 رو فشار بدید و پیاده سازیش رو ببینید، فقط یک delegate ساده است که پارامتر ورودی نداره. اگه بجای ThreadStart از ParameterizedThreadStart استفاده کنید، متد تون میتونه یک پارامتر از نوع object داشته باشه :
کد:
        private void buttonStart_Click(object sender, EventArgs e)
        {
            var start = new System.Threading.ParameterizedThreadStart(Test2);
            _thread = new System.Threading.Thread(start);
            _thread.Start("hello");
        }

        private void Test2(object data)
        {
        }



بعد اینکه چرا عضو protected از یک کلاس ، فقط وقتی درون کلاس فرزند ، از همون کلاس فرزند شی بسازیم در دسترس هه؟ یعنی به چه دلیل وقتی از کلاس فرزند ، شی ای از کلاس پدر بسازیم ، اعضای protected تعریف شده از کلاس پدر در دسترس نیست؟
ممنون
دلیلش اینه که شما دسترسی رو مستقل از اشیاء فرض می کنید، در حالی که دسترسی بر حسب اشیاء هم هست، نه فقط کلاس.
protected یعنی دسترسی فقط داخل خود همون کلاس والد و کلاس های وارث. اما برای مشخص کردن اینکه از کجا به کجا دسترسی دارید باید ببینید عضو static هست یا نیست.
اگه عضو مورد نظر تون static protected باشه و در نتیجه ربطی به شیء نداشته باشه، شما در هر جایی از کلاس های فرزند می توانید بهش دسترسی داشته باشید، حالا فقط با اسم عضو خالی یا درج نام کلاس والد قبل از اون :
کد:
    class Class1
    {
        static protected int X = 0;
    }

    class Class2 : Class1
    {
        void Test()
        {
            X = 4;
            Class1.X = 5;
        }
    }

اما اگه عضو مورد نظر تون static نباشه، فقط در همون شیء از کلاس والد و اشیاء وارث همون شیء قابل دسترسی ئه. شما میخواهید از یک شیء وارث دیگه به شیء ای از کلاس والد دسترسی داشته باشید که ربطی به base نداره و مستقل ئه، protected همچین دسترسی ای رو ایجاد نمی کنه. شما فقط می توانید در اون شیء وارث به عضو های protected ای دسترسی داشته باشید که شیء والدش یا همون base داشته، نه هر شیء ای از کلاس والد.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
تا کد رو نبینم هیچ نظری نمیتونم بدم، توضیحات تون کمکی نمی کنه.

ممنون
کلا فراموش کردم فایل رو آپلود کنم (این قدر حواس پرتم که) :)
بفرمایید . فایل رو توی همین پست ، پیوست کردم


روی ThreadStart کلید F12 رو فشار بدید و پیاده سازیش رو ببینید، فقط یک delegate ساده است که پارامتر ورودی نداره. اگه بجای ThreadStart از ParameterizedThreadStart استفاده کنید، متد تون میتونه یک پارامتر از نوع object داشته باشه :
کد:
        private void buttonStart_Click(object sender, EventArgs e)
        {
            var start = new System.Threading.ParameterizedThreadStart(Test2);
            _thread = new System.Threading.Thread(start);
            _thread.Start("hello");
        }

        private void Test2(object data)
        {
        }


دلیلش اینه که شما دسترسی رو مستقل از اشیاء فرض می کنید، در حالی که دسترسی بر حسب اشیاء هم هست، نه فقط کلاس.
protected یعنی دسترسی فقط داخل خود همون کلاس والد و کلاس های وارث. اما برای مشخص کردن اینکه از کجا به کجا دسترسی دارید باید ببینید عضو static هست یا نیست.
اگه عضو مورد نظر تون static protected باشه و در نتیجه ربطی به شیء نداشته باشه، شما در هر جایی از کلاس های فرزند می توانید بهش دسترسی داشته باشید، حالا فقط با اسم عضو خالی یا درج نام کلاس والد قبل از اون :
کد:
    class Class1
    {
        static protected int X = 0;
    }

    class Class2 : Class1
    {
        void Test()
        {
            X = 4;
            Class1.X = 5;
        }
    }

اما اگه عضو مورد نظر تون static نباشه، فقط در همون شیء از کلاس والد و اشیاء وارث همون شیء قابل دسترسی ئه. شما میخواهید از یک شیء وارث دیگه به شیء ای از کلاس والد دسترسی داشته باشید که ربطی به base نداره و مستقل ئه، protected همچین دسترسی ای رو ایجاد نمی کنه. شما فقط می توانید در اون شیء وارث به عضو های protected ای دسترسی داشته باشید که شیء والدش یا همون base داشته، نه هر شیء ای از کلاس والد.

ممنون
ولی قشنگ حالی ام نشد :green:
با قسمت static بودن اش مشکلی ندارم و میدونم
 

پیوست ها

  • Practice 1.rar
    142.7 کیلوبایت · بازدیدها: 3

the_king

مدیرکل انجمن
ممنون
کلا فراموش کردم فایل رو آپلود کنم (این قدر حواس پرتم که) :)
بفرمایید . فایل رو توی همین پست ، پیوست کردم
شما تو کد تون new ندارید که :
کد:
        private void btnThread1_Click(object sender, EventArgs e)
        {
            var start = System.Threading.ThreadStart(MessageThread1);
            var thread = System.Threading.Thread(start);
        }
میخواهید شیء جدید بسازید، new لازمه :
کد:
        private void btnThread1_Click(object sender, EventArgs e)
        {
            var start = new System.Threading.ThreadStart(MessageThread1);
            var thread = new System.Threading.Thread(start);
        }
ممنون
ولی قشنگ حالی ام نشد :green:
با قسمت static بودن اش مشکلی ندارم و میدونم
عامیانه اش اینه، شما وقتی تو کلاس پدر عضو اتومبیل رو protected می کنید یعنی وارث اش که فرزندشه به این اتومبیل پدر دسترسی داره، ولی به این معنی نیست که هر پسری به اتومبیل همه پدر ها دسترسی داره، فقط به اتومبیل بابای خودش دسترسی داره. شما در کلاس فرزند یک شیء پدر رو در نظر گرفته اید، مثلا بابای دوستش، بعد انتظار دارید که این پسر به اتومبیل بابای دوستش دسترسی داشته باشه، خوب نمیشه دیگه :green:
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلام استاد علی
من واقعا ازتون عذر میخوام که این قدر سئوال میپرسم . :)
درباره ی اون new آره . من بازم فراموش کردم . این قدر حواس پرتم :)
-------------------------------------------------
یه مشکلی که تا حالا دارم برای زمانی هست که از نوع یه کلاس پدر یه شی ای در نظر میگیریم ولی مقدارش رو ، مقداری از فرزند یا نوادگان اش در نظر میگیریم . مثلا :


کد:
namespace Practice_1
{
    public partial class Form1 : Form
    {
private void btnFatherTypeChildObject1_Click(object sender, EventArgs e)
        {
            Test1 test1Obj = new Test2();

        }
    }


    public class Test1 : Form, IMyTest1
    {
        public string memberInt1 = "salam";

        public Test1()
        {
            MessageBox.Show("Class Test1 Constractor");
        }

        public virtual int VirtualMethod1(int a)
        {
            a *= a;
            return a;
        }
        public string Show()
        {
            return "سلام . خوبی؟";
        }
      
        public void Method1()
        {

        }

        public string MyMethod1(string entryString)
        {
            return entryString + "  سلام";
        }
    }



    public class Test2 : Test1
    {
        private int a = 10;
        public Test2()
        {
            MessageBox.Show("constractor Test2 class");
        }

        public override int VirtualMethod1(int a)
        {
            a += a;
            return a;
        }

    }

}


در این باره ، یعنی درباره کد :

کد:
Test1 test1Obj = new Test2();

در بالا ، این چیزها رو میگم ، درسته ؟ و اگه نکته ای و چیزی رو نمیدونم ، ممنون میشم بگین :
اینکه وقتی مقداری رو ایجاد میکنیم (همون سمت راست مساوی) یعنی وقتی new Test2() رو مینویسیم ، این مقدار ، در واقع اشاره گری هست از حافظه ی استک به خونه ی کلاس مورد نظرش در حافظه ی هیپ رم . پس در واقع شی test1Obj در بالا ، به اشیاء در کلاس فرزند (سمت راست خودش یعنی همون مقدارش) دسترسی داره و فقط اعضای این کلاس فرزند (کلاس Test2) رو اجرا میکنه (نه اینکه اعضای کلاس پدر یعنی کلاس Test1 رو اجرا کنه) منتها چون شی اش از نوع کلاس Test1 هست ، پس فقط اعضایی از کلاس Test2 در دسترس اش هست که فقط در کلاس پدر یعنی در کلاس Test1 تعریف شده باشن پس فقط اعضایی که در کلاس فرزند ، جدید تعریف شده باشن (یعنی در کلاس پدر نباشن) ، در این شی در دسترس مون نیست
بنابراین ، وقتی یه عضوی در کلاس پدر ، بصورت virtual تعریف شه ، در این شی test1Obj ، در صورت override شدن در کلاس فرزند ، همین عضو override شده در دسترس مون قرار میگیره چون override کردن باعث میشه عضو override شده در کلاس پدر ، باطل بشه
البته در این شی test1Obj ، فقط قضیه ی member hiding برعکس هست که نمیدونم چرا ، عضو member hide در کلاس پدر رو اجرا میکنه؟!

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

تحلیل بالا درسته (که به نظرم باید تحلیل بالا درست باشه) یا اینکه این تحلیل درسته؟ :
در این شی test1Obj ، همه ی اعضای فرزند ای که در کلاس پدر تعریف شدن ، اجرا میشن . یعنی در واقع ، همه ی اعضای کلاس پدر اجرا میشن بجز در قضیه ی اعضای virtual شده که این اعضا (اعضای override شده) از کلاس فرزند اجرا میشن ولی ما بقی اعضا از کلاس پدر

باز هم عذر میخوام و متشکرم
 

the_king

مدیرکل انجمن
یه مشکلی که تا حالا دارم برای زمانی هست که از نوع یه کلاس پدر یه شی ای در نظر میگیریم ولی مقدارش رو ، مقداری از فرزند یا نوادگان اش در نظر میگیریم . مثلا :

کد:
namespace Practice_1
{
    public partial class Form1 : Form
    {
private void btnFatherTypeChildObject1_Click(object sender, EventArgs e)
        {
            Test1 test1Obj = new Test2();

        }
    }


    public class Test1 : Form, IMyTest1
    {
        public string memberInt1 = "salam";

        public Test1()
        {
            MessageBox.Show("Class Test1 Constractor");
        }

        public virtual int VirtualMethod1(int a)
        {
            a *= a;
            return a;
        }
        public string Show()
        {
            return "سلام . خوبی؟";
        }
     
        public void Method1()
        {

        }

        public string MyMethod1(string entryString)
        {
            return entryString + "  سلام";
        }
    }



    public class Test2 : Test1
    {
        private int a = 10;
        public Test2()
        {
            MessageBox.Show("constractor Test2 class");
        }

        public override int VirtualMethod1(int a)
        {
            a += a;
            return a;
        }

    }

}


در این باره ، یعنی درباره کد :

کد:
Test1 test1Obj = new Test2();

در بالا ، این چیزها رو میگم ، درسته ؟ و اگه نکته ای و چیزی رو نمیدونم ، ممنون میشم بگین :
اینکه وقتی مقداری رو ایجاد میکنیم (همون سمت راست مساوی) یعنی وقتی new Test2() رو مینویسیم ، این مقدار ، در واقع اشاره گری هست از حافظه ی استک به خونه ی کلاس مورد نظرش در حافظه ی هیپ رم . پس در واقع شی test1Obj در بالا ، به اشیاء در کلاس فرزند (سمت راست خودش یعنی همون مقدارش) دسترسی داره و فقط اعضای این کلاس فرزند (کلاس Test2) رو اجرا میکنه (نه اینکه اعضای کلاس پدر یعنی کلاس Test1 رو اجرا کنه) منتها چون شی اش از نوع کلاس Test1 هست ، پس فقط اعضایی از کلاس Test2 در دسترس اش هست که فقط در کلاس پدر یعنی در کلاس Test1 تعریف شده باشن پس فقط اعضایی که در کلاس فرزند ، جدید تعریف شده باشن (یعنی در کلاس پدر نباشن) ، در این شی در دسترس مون نیست
بنابراین ، وقتی یه عضوی در کلاس پدر ، بصورت virtual تعریف شه ، در این شی test1Obj ، در صورت override شدن در کلاس فرزند ، همین عضو override شده در دسترس مون قرار میگیره چون override کردن باعث میشه عضو override شده در کلاس پدر ، باطل بشه
البته در این شی test1Obj ، فقط قضیه ی member hiding برعکس هست که نمیدونم چرا ، عضو member hide در کلاس پدر رو اجرا میکنه؟!

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

تحلیل بالا درسته (که به نظرم باید تحلیل بالا درست باشه) یا اینکه این تحلیل درسته؟ :
در این شی test1Obj ، همه ی اعضای فرزند ای که در کلاس پدر تعریف شدن ، اجرا میشن . یعنی در واقع ، همه ی اعضای کلاس پدر اجرا میشن بجز در قضیه ی اعضای virtual شده که این اعضا (اعضای override شده) از کلاس فرزند اجرا میشن ولی ما بقی اعضا از کلاس پدر

باز هم عذر میخوام و متشکرم
مرحله به مرحله بریم جلو بهتره، اولین نکته ای در که باید در نظر بگیریم اینه که شیء ای که با new ساخته میشه وقتی در متغیری از نوع والدش قرار بگیره، همچنان از نوع همون شیء ای است که با new ساخته شده، یعنی ویژگی های فرزند واقعا حذف نمیشه، صرفا کامپایلر اونرو به عنوان شیء ای از نوع والد قبول می کنه ولی جلوی اجرا شدن متد ها با شرایط کلاس فرزند رو نمی گیره، ابدا به شیوه کلاس والد اجراشون نمی کنه. مثال ساده اینه :
کد:
    class Class1
    {
        public int A;
    }

    class Class2 : Class1
    {
        public int B;
    }
بدیهی است که یک شیء از کلاس Class1 نمیتونه به Class2 تبدیل بشه، پس خطا در انتساب m رخ خواهد داد :
کد:
            Class1 c = new Class1();
            Class2 m = (Class2)c;
اما اگر کد زیر رو اجرا کنید، هیچ خطایی برای انتساب متغیر m رخ نمیده چون شیء ای که در c قرار داره واقعا یک شیء از نوع Class2 است، با وجود اینکه نوع متغیر c ظاهرا Class1 بود :
کد:
            Class1 c = new Class2();
            Class2 m = (Class2)c;
این مساله نشون میده که c با امانت داری کامل فقط اشاره گر شیء رو نگهداری کرده، درسته که کامپایلر c رو از نوع Class1 میدونه ولی ماهیت شیء همون Class2 میمونه. وقتی از متد های override شده استفاده می کنید این مورد بهتر خودش رو نشون میده که اینجا نوع داده ای که در new مشخص می کنید مهمه، نه نوع متغیری که شیء رو داخلش قرار می دهید :
کد:
    class Class1
    {
        public virtual void Test()
        {
            MessageBox.Show("Class1");
        }
    }

    class Class2 : Class1
    {
        public override void Test()
        {
            MessageBox.Show("Class2");
        }
    }
وقتی کد زیر رو اجرا می کنید، پیغام "Class2" نمایش داده میشه، چون c واقعا یک شیء Class2 است، نوع متغیر نمیتونه جلوی اجرا شدن نسخه override رو بگیره :
کد:
            Class1 c = new Class2();
            c.Test();
دقت کنید که override شدن و نشدن یک عضو کاری به متد سازنده کلاس نداره، شما اگر بخواهید شیء ای از کلاس Class2 بسازید و new اجرا بشه، طبق روال سلسله مراتبی ساختن شیء باید ابتدا متد سازنده Class1 اجرا بشه تا بعد نوبت به متد سازنده Class2 برسه، دیگه نمی توانید این متد سازنده Class1 رو override کنید یا به تعبیر خودتان باطل کنید، تا این متد سازنده اجرا نشه شیء Class2 ای در کار نیست.
 

SajjadKhati

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

کد:
namespace Practice_1
{
    public delegate MatchCollection Messager(string a);
    public partial class Form1 : Form
    {
        public event Messager myMessanger2;

        public Form1()
        {
            InitializeComponent();
            myMessanger2 = new Messager( (new Father()).EventExecuteMe);
        }

        private void btnCallerEvent2_Click(object sender, EventArgs e)
        {
            MatchCollection numberInText = myMessanger2(txtboxGeneralUsage.Text);
        }
    }

    public class Father
    {
        public MatchCollection EventExecuteMe(string changeText)
        {
            MessageBox.Show("EventExecuteMe Method In Father Class :\n\n" + changeText);
            Regex pattern = new Regex("\\d+");
            MatchCollection numberInchangeText = pattern.Matches(changeText);
            return numberInchangeText;
        }
    }
}

چرا در متد سازنده ی کلاس Form1 و در موقع مقداردهی شی myMessanger2 ، با اونکه این شی ، یک event تعریف شده ، ولی عملگر = رو قبول کرد و اشکالی نگرفت و کدش کار میکنه؟

سوم اینکه در قضیه ی کدهای Thread (نخ) که دادین ، ببینین درست میگم :
کلا نخ ها ، ورودی متد میخوان و بعد از ایجاد نخ ، میشه با متد Start از شی این نخ ها ، این متد ها رو اجرا کرد . خوب میدونیم که دلیگیت ها همون اشاره گر به متدهان پس میشه چندین متد از یه دلیگیت اجرا کرد بناراین بهتره که بجای متد ، از دلیگیت برای اشیاء نخ ها استفاده بشه که میشه
درسته دیگه؟

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

کد:
namespace Practice_1
{
    public delegate MatchCollection Messager(string a);
    public partial class Form1 : Form
    {
        ThreadStart threadDelegate;
        Thread suspendThread;

        public Form1()
        {
            InitializeComponent();
            threadDelegate = new ThreadStart(SuspendThread2);
        }

        public void SuspendThread2()
        {
            Thread.Sleep(1500); 
            MessageBox.Show("1");
            suspendThread.Suspend();
            MessageBox.Show("2");
            suspendThread.Suspend();
            MessageBox.Show("3");
            suspendThread.Suspend();
            MessageBox.Show("4");

        }

        private void btnThread3Timer_Suspend_Click(object sender, EventArgs e)
        {
            suspendThread = new Thread(threadDelegate);
            suspendThread.Start();
            timerSuspendThread.Enabled = true;
        }

        private void timerSuspendThread_Tick(object sender, EventArgs e)
        {
            suspendThread.Resume(); 
        }
    }
}

به نظرم نسبت به کدهای لوا ، 3 تا مشکل داره که ممنون میشم برای حل اش راهنمایی کنین . توی لوا ، وقتی متد Resume رو مینوشتیم ، خودش میفهمید ، این بار اولشه که این نخ اجرا شد یا بار دوم به بعد و به نسبت اون ، اقدام میکرد و اجرا میشد ولی اینجا برای بار اول حتما باید متد Start رو بنویسیم و برای بار دوم به بعد هم حتما باید متد Resume رو وگرنه ارور میده . هر چند این قضیه اش زیاد مهم نیست . مشکل دوم اینکه توی لوا هر بار که متد Resume اجرا میشد ، اول میرفت نخ رو اجرا میکرد و بعد توقف کد داخل نخ ، میومد ادامه ی کدها رو اجرا میکرد ولی اینجا وقتی مینویسیم :
کد:
suspendThread.Start();

suspendThread.Resume();
کد بالا اگه توی یک رویداد نوشته شده باشه و وقتی خط اول رو اجرا کنه ، نمیره نخ رو اجرا کنه واسه همین با اجرای کد خط دوم ، ارور پیش میاد که فقط چاره اش این میشه که هر کد رو توی رویدادهای متفاوت بنویسیم . این مشکل برم مهم هست و نمیشه کاری کرد که در یک رویداد ، کدهای متفاوتی از نخ رو اجرا کرد مثل بالا؟
سوم اینکه توی لوا ، متد Suspend که باعث توقف کد (درون نخ) میشه ، میتونست رشته ای برگردونه که وضعیت اجرای نخ (به تعلیق در آمده یا تمام شده و ...) رو گزارش کنه و همینطور میتونست از متدهای دیگه ، رشته ها رو برگردونه و به متد Resume در رویدادی که باعث فراخونی اش شد ، برگردونه ولی اینجا متد Suspend نمیتونه وضعیت و چیزی رو برگردونه . برای برگردوندن وضعیت ، یا حتی در صورت اتمام نخ وقتی نخ (متد) مون ، چیزی رو برگردونه ، چجوری باید متد Resume (یا متد Start) که نخ رو به اتمام رسوند ، از مقدار بازگشتی نخ مورد نظر مطلع بشه؟



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

شیشم اینکه من خیلی از متد استاتیک Sleep که در کلاس Thread استفاده کردین ، خوشم اومد . در اتوپلی ، این متد ، هم مصرف پردازنده رو بسیار بالا میبرد و هم باعث not respond شدن برنامه تا پایان اجرای این کد میشد ولی در اینجا هیچ کدوم از این مشکلات رو نداره!

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

هشتم اینکه ، یه قضیه ی Attributes رو خوب درک کنم و اینکه کلا چیه ، عالی میشد (البته هنوز درباره اش تحقیقات اولیه نکردم)

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

آخر نشد به ده تا برسه :green:
ممنون استاد علی . بازم عذر میخوام
:rose:
 

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

بالا