اجرای ناقص دستور foreach

MRHADI

Member
سلام دوستان
هدفم از کد زیر این هست که به ازای هر آرایه یک کلید(BUTTON) ساخته بشه
این کار به درستی انجام میشه اما به شرطی که در foreach چیز دیگه ای نباشه
مثلا اگر var PD باشه طبق کد،قاطی میکنه،خطایی نمیگیره اما یه دفعه بدون کلید میسازه یه دفعه با دوتا یه بار با سه تا و...
به نظرتون اشکال کجاست؟


کد:
        protected override void Initialize()
        {

            #region
            _f1 = new Form1();

            _thread = new Thread(() => _f1.ShowDialog());

            _thread.SetApartmentState(ApartmentState.STA);

            _thread.Start();

            _f1.Text = SymbolName;
            #endregion

            var symbolNames = this.SymbolsText.Split(new[]
            {
                ','
            }).Select(s => s.Trim()).Where(s => string.IsNullOrWhiteSpace(s) == false).ToArray();


            foreach (var SN in symbolNames)
            {
                var PD = MarketData.GetBars(TimeFrame, SN);
                System.Windows.Forms.Button BTN1 = new System.Windows.Forms.Button
                {
                    Text = SN,
                    Enabled = SN != SymbolName

                };


                _f1.flowLayoutPanel1.Controls.Add(BTN1);
                BTN1.Click += BTN1_Click;
                if (BTN1.Enabled == false)
                    BTN1.BackColor = System.Drawing.Color.LightGreen;


            }
 

the_king

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


کد:
        protected override void Initialize()
        {

            #region
            _f1 = new Form1();

            _thread = new Thread(() => _f1.ShowDialog());

            _thread.SetApartmentState(ApartmentState.STA);

            _thread.Start();

            _f1.Text = SymbolName;
            #endregion

            var symbolNames = this.SymbolsText.Split(new[]
            {
                ','
            }).Select(s => s.Trim()).Where(s => string.IsNullOrWhiteSpace(s) == false).ToArray();


            foreach (var SN in symbolNames)
            {
                var PD = MarketData.GetBars(TimeFrame, SN);
                System.Windows.Forms.Button BTN1 = new System.Windows.Forms.Button
                {
                    Text = SN,
                    Enabled = SN != SymbolName

                };


                _f1.flowLayoutPanel1.Controls.Add(BTN1);
                BTN1.Click += BTN1_Click;
                if (BTN1.Enabled == false)
                    BTN1.BackColor = System.Drawing.Color.LightGreen;


            }
نمیدونم منبع MarketData.GetBars چیه و ایراد داره یا نه، اما شما ابتدا در یک نخ مجزا f1_ رو نمایش می دهید که اون نخ ئه اداره کننده فرم و کنترل هایش خواهد شد و بعد می آیید داخل نخ دیگری که Initialize رو اجرا می کنه به کنترل هایش دسترسی پیدا می کنید و فرضا Button درج می کنید. نباید اینکار رو بکنید. دو تا نخ مجزا هستند، اگر می خواهید کاری رو انجام بدید که به اون فرم مربوط ئه، باید Invoke کنید تا نخ اداره کننده همون فرم اجراش کنه.
اون ApartmentState.STA برای دسترسی اشیاء COM مثل ActiveX ها تاثیر داره اما خود NET. با معماری Apartments کار نمی کنه.
از طرف دیگه شما به محض اینکه thread_ رو Start کردید می روید سراغ ادامه کد و درج کردن Button روی اون. نخ برای ساختن و نمایش فرم به زمان نیاز داره، الزاما وقتی شما دارید بهش دکمه اضافه می کنید هنوز آمادگی اینکار رو نداره، باید بهش مهلت بدید.
کد:
            #region
            _f1 = new Form1();

            _f1.Shown += _f1_Shown;

            _thread = new Thread(() => _f1.ShowDialog());

            _thread.SetApartmentState(ApartmentState.STA);

            _thread.Start();

            #endregion


        private void _f1_Shown(object sender, EventArgs e)
        {
            _f1.Text = SymbolName;
            DoSomething();
        }

        private void DoSomething()
        {
            var symbolNames = this.SymbolsText.Split(new[]
            {
                ','
            }).Select(s => s.Trim()).Where(s => string.IsNullOrWhiteSpace(s) == false).ToArray();
            foreach (var SN in symbolNames)
            {
                var PD = MarketData.GetBars(TimeFrame, SN);
                var BTN1 = new System.Windows.Forms.Button
                {
                    Text = SN,
                    Enabled = SN != SymbolName

                };

                BTN1.Click += BTN1_Click;
                if (BTN1.Enabled == false)
                    BTN1.BackColor = System.Drawing.Color.LightGreen;
                _f1.flowLayoutPanel1.Controls.Add(BTN1);
            }
        }

به این نکته توجه کنید که f1_Shown_ و DoSomething توسط نخ ای اجرا میشه که ساختید و فرم رو نمایش میده، نه نخی که باهاش Initialize رو اجرا می کردید.
برای همین در دسترسی به کنترل های روی f1_ مشکلی نداره چون اداره کننده اش خودشه. اگر احیانا بخواهید در این میان کدی رو اجرا کنید که به نخی که Initialize رو اجرا می کنه مربوط بود باید this.Invoke رو اجرا کنید.
 

MRHADI

Member
نمیدونم منبع MarketData.GetBars چیه و ایراد داره یا نه، اما شما ابتدا در یک نخ مجزا f1_ رو نمایش می دهید که اون نخ ئه اداره کننده فرم و کنترل هایش خواهد شد و بعد می آیید داخل نخ دیگری که Initialize رو اجرا می کنه به کنترل هایش دسترسی پیدا می کنید و فرضا Button درج می کنید. نباید اینکار رو بکنید. دو تا نخ مجزا هستند، اگر می خواهید کاری رو انجام بدید که به اون فرم مربوط ئه، باید Invoke کنید تا نخ اداره کننده همون فرم اجراش کنه.
اون ApartmentState.STA برای دسترسی اشیاء COM مثل ActiveX ها تاثیر داره اما خود NET. با معماری Apartments کار نمی کنه.
از طرف دیگه شما به محض اینکه thread_ رو Start کردید می روید سراغ ادامه کد و درج کردن Button روی اون. نخ برای ساختن و نمایش فرم به زمان نیاز داره، الزاما وقتی شما دارید بهش دکمه اضافه می کنید هنوز آمادگی اینکار رو نداره، باید بهش مهلت بدید.
کد:
            #region
            _f1 = new Form1();

            _f1.Shown += _f1_Shown;

            _thread = new Thread(() => _f1.ShowDialog());

            _thread.SetApartmentState(ApartmentState.STA);

            _thread.Start();

            #endregion


        private void _f1_Shown(object sender, EventArgs e)
        {
            _f1.Text = SymbolName;
            DoSomething();
        }

        private void DoSomething()
        {
            var symbolNames = this.SymbolsText.Split(new[]
            {
                ','
            }).Select(s => s.Trim()).Where(s => string.IsNullOrWhiteSpace(s) == false).ToArray();
            foreach (var SN in symbolNames)
            {
                var PD = MarketData.GetBars(TimeFrame, SN);
                var BTN1 = new System.Windows.Forms.Button
                {
                    Text = SN,
                    Enabled = SN != SymbolName

                };

                BTN1.Click += BTN1_Click;
                if (BTN1.Enabled == false)
                    BTN1.BackColor = System.Drawing.Color.LightGreen;
                _f1.flowLayoutPanel1.Controls.Add(BTN1);
            }
        }

به این نکته توجه کنید که f1_Shown_ و DoSomething توسط نخ ای اجرا میشه که ساختید و فرم رو نمایش میده، نه نخی که باهاش Initialize رو اجرا می کردید.
برای همین در دسترسی به کنترل های روی f1_ مشکلی نداره چون اداره کننده اش خودشه. اگر احیانا بخواهید در این میان کدی رو اجرا کنید که به نخی که Initialize رو اجرا می کنه مربوط بود باید this.Invoke رو اجرا کنید.
سلام
نمیدونید چقدر بابت این داستان اذیت شدم و درگیرش بودم و هیچ کجا هم کمک نمیکردن تا این سایت رو پیدا کردم،خدا خیرتون بده
بالاخره حل شد،لطف کردین،دو تا خواهش دارم
یکی اینکه یک منبعی برای یادگیری این قلق ها بفرمایید و دوم اینکه سرعت اجرای فرم یکم پایینه بفرمایید آیا ممکنه سرعتش رو افزایش بدم؟
 

the_king

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

MRHADI

Member
معمولا اینجور موارد بصورت متفرقه و در سوالات کاربران مطرح میشه، مثلا در سایت های پرسش و پاسخ مثل stackoverflow.com ، برای همین جستجو در گوگل شاید راحت تر باشه.
سرعت اجرای کدتون طبعا وابسته به الگوریتم کدی است که می نویسید، اگه مثال از کدی بزنید که اجرای کندی داره طبعا میشه رویش تمرکز کرد.
ممنون
کدها چیز خاصی نیست که سرعت زیادی بطلبه ، همین کدی رو که شما زحمت کشیدید کپی کردم و یکم تغییرات جزئی،گفتم ببینم آیا توسط دستور Thread راهی هست که بتونم سرعتش رو زید کنم یا خیر
بازم زحمت دارم براتون
1-نرم افزاری که قراره این کد رو اجرا کنه اسمش ctrader هست که مال بورسه ، حالا من اگه روی کلیدهای این فرم بزنم فرم من بسته میشه و دوباره باز میشه ، راهی هست که بدون بسته شدن کلیدها رو اجرا کنه؟
2-بعضی مواقع هم هست که این فرم وقتی آپدیت میشه یا تغییری درش انجام میشه در ctrader ، بدون اینکه بسته بشه یکی دیگه باز میکنه و در طول کار چندین فرم ممکنه باز بشه،چه کدی رو باید بنویسم تا وقتی فرم جدیدی میخواد باز بشه
حتما حتما فرم همنام خودش رو ببنده؟
3-من کلیدها رو به صورت دستی نوشتم تا بتونم در دستور foreach ازش new بگیرم،الان تنظیماتش خیلی سخته ، چطور بیام و یک button روی فرم drag کنم و بعد دوبار در foreach ازش new بگیرم؟
4-توسط چه کدی میتونم این فرم و یا اطلاعات داخش رو هر n دقیقه آپدیت کنم یا همون رفرش کنم
ببخشید زیاد شد
تازه دارم راه میوفتم مقدمات کار کردم و فکر میکنم اواخر متوسط هستم
 

the_king

مدیرکل انجمن
ممنون
کدها چیز خاصی نیست که سرعت زیادی بطلبه ، همین کدی رو که شما زحمت کشیدید کپی کردم و یکم تغییرات جزئی،گفتم ببینم آیا توسط دستور Thread راهی هست که بتونم سرعتش رو زید کنم یا خیر
بازم زحمت دارم براتون
1-نرم افزاری که قراره این کد رو اجرا کنه اسمش ctrader هست که مال بورسه ، حالا من اگه روی کلیدهای این فرم بزنم فرم من بسته میشه و دوباره باز میشه ، راهی هست که بدون بسته شدن کلیدها رو اجرا کنه؟

چون از این منطق و دلیل باز و بسته شدن فرم اطلاعی ندارم نمیتونم نظری بدم.

2-بعضی مواقع هم هست که این فرم وقتی آپدیت میشه یا تغییری درش انجام میشه در ctrader ، بدون اینکه بسته بشه یکی دیگه باز میکنه و در طول کار چندین فرم ممکنه باز بشه،چه کدی رو باید بنویسم تا وقتی فرم جدیدی میخواد باز بشه
حتما حتما فرم همنام خودش رو ببنده؟
در اغلب موارد که هر فرم متغیر های مستقل خودش رو داره مهم نیست که چندین شیء از اون کلاس فرم ایجاد بشه، می توانید از یک Form1 چهار نمونه باز داشته باشید.
اما اگر فرم جدیدی رو باز می کنید که دیگه به فرم های قبلا باز شده نیازی نیست و لازم هم نیست که همچنان باز بمونند، حتما Dispose اش کنید و ببندیدش.
مثلا هر فرم Form1 باز شده ای بجز همین فرم که کد رو اجرا می کنه بسته بشه. از Invoke استفاده می کنیم تا مطمئن بشویم که نخ اجرا کننده نخ ای بجز نخ مدیریت کننده فرم نباشه :
کد:
        private void button1_Click(object sender, EventArgs e)
        {
            var forms = new List<Form>();
            foreach (Form f in Application.OpenForms)
            {
                if ((f is Form1) && (f != this))
                {
                    forms.Add(f);
                }
            }
            foreach (var f in forms)
            {
                f.Invoke(new System.Threading.ParameterizedThreadStart(CloseForm), f);
            }
        }

        private void CloseForm(object f)
        {
            ((Form)f).Dispose();
        }

3-من کلیدها رو به صورت دستی نوشتم تا بتونم در دستور foreach ازش new بگیرم،الان تنظیماتش خیلی سخته ، چطور بیام و یک button روی فرم drag کنم و بعد دوبار در foreach ازش new بگیرم؟
مطمئن نیستم که متوجه منظورتون شده باشم.
بعضی کلاس ها یک متد Clone آماده دارند که برای تکثیر شیء مشابه شون بدرد میخوره، مثلا یک شیء Font داریم، با متد Clone ازش یک شیء کپی گرفته میشه که مشخصاتش شبیه همون قبلی است.
اما در مورد کلاس هایی مثل Button همچین متد آماده ای نیست و متد Clone نداره. پس خودتون باید تک تک مشخصه هایی که لازمه (مثلا Font یا Size یا ...) رو از شیء قبلی در شیء جدید کپی کنید.


4-توسط چه کدی میتونم این فرم و یا اطلاعات داخش رو هر n دقیقه آپدیت کنم یا همون رفرش کنم
ببخشید زیاد شد
تازه دارم راه میوفتم مقدمات کار کردم و فکر میکنم اواخر متوسط هستم
همه اون کد هایی که برای نشان دادن مقادیر در فرم نوشته اید رو جمع کنید در یک متد که فراخوانی مجدد اش راحت باشه، مثلا یک متد جدید به اسم ShowData .
بعد یک Timer زیر فرم قرار بدهید و Enabled اش رو True کنید و Interval اش رو n * 1000 میلی ثانیه قرار بدید و روی Timer دوبار کلیک کنید و در رخداد timer_Tick اش اون متد ShowData رو فراخوانی کنید. هر n * 1000 میلی ثانیه یکبار اجرا میشه.
 

MRHADI

Member
چون از این منطق و دلیل باز و بسته شدن فرم اطلاعی ندارم نمیتونم نظری بدم.


در اغلب موارد که هر فرم متغیر های مستقل خودش رو داره مهم نیست که چندین شیء از اون کلاس فرم ایجاد بشه، می توانید از یک Form1 چهار نمونه باز داشته باشید.
اما اگر فرم جدیدی رو باز می کنید که دیگه به فرم های قبلا باز شده نیازی نیست و لازم هم نیست که همچنان باز بمونند، حتما Dispose اش کنید و ببندیدش.
مثلا هر فرم Form1 باز شده ای بجز همین فرم که کد رو اجرا می کنه بسته بشه. از Invoke استفاده می کنیم تا مطمئن بشویم که نخ اجرا کننده نخ ای بجز نخ مدیریت کننده فرم نباشه :
کد:
        private void button1_Click(object sender, EventArgs e)
        {
            var forms = new List<Form>();
            foreach (Form f in Application.OpenForms)
            {
                if ((f is Form1) && (f != this))
                {
                    forms.Add(f);
                }
            }
            foreach (var f in forms)
            {
                f.Invoke(new System.Threading.ParameterizedThreadStart(CloseForm), f);
            }
        }

        private void CloseForm(object f)
        {
            ((Form)f).Dispose();
        }


مطمئن نیستم که متوجه منظورتون شده باشم.
بعضی کلاس ها یک متد Clone آماده دارند که برای تکثیر شیء مشابه شون بدرد میخوره، مثلا یک شیء Font داریم، با متد Clone ازش یک شیء کپی گرفته میشه که مشخصاتش شبیه همون قبلی است.
اما در مورد کلاس هایی مثل Button همچین متد آماده ای نیست و متد Clone نداره. پس خودتون باید تک تک مشخصه هایی که لازمه (مثلا Font یا Size یا ...) رو از شیء قبلی در شیء جدید کپی کنید.



همه اون کد هایی که برای نشان دادن مقادیر در فرم نوشته اید رو جمع کنید در یک متد که فراخوانی مجدد اش راحت باشه، مثلا یک متد جدید به اسم ShowData .
بعد یک Timer زیر فرم قرار بدهید و Enabled اش رو True کنید و Interval اش رو n * 1000 میلی ثانیه قرار بدید و روی Timer دوبار کلیک کنید و در رخداد timer_Tick اش اون متد ShowData رو فراخوانی کنید. هر n * 1000 میلی ثانیه یکبار اجرا میشه.
در مورد 1 حق دارین چون عمومی نیست و توسط نرم افزار دیگه ای لود میشه
مورد سوم رو هم من نتونستم درست منظورم رو برسونم،سعی میکنم روی اون کار کنم
مورد 4 رو اگر زحمتی نیست برام مثال بزنید
بی نهایت سپاسگذارم
 

the_king

مدیرکل انجمن
مورد 4 رو اگر زحمتی نیست برام مثال بزنید
بی نهایت سپاسگذارم
مثلا در فرم چند تا Label و TextBox و ... داده هایی رو از جایی می خوانند و نمایش می دهند. اون تایمر timer1 ای که از Toolbox روی فرم Drag می کنید، در فواصل زمانی مشخصی نمایش رو رفرش میکنه :
کد:
        private void ShowData()
        {
            label1.Text = MyData.A;
            label1.Text = MyData.B;
            textBox1.Text = MyData.C;
            textBox2.Text = MyData.D;
            .
            .
            .
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            ShowData();
        }
 

MRHADI

Member
ببخشید یک سوال دیگه
من در نرم افزار ctrader که تحت سی شارپ هم هست کاری که button باید انجام بده رو به صورت زیر مینوشتم و کار هم میداد
ولی الان ویژال استودیو این رو قبول نمیکنه
راهی هست بدون اینکه یک متد مجزا بنویسم بتونم رویداد button رو تعریف کنم؟
چون در همون کدی که زحمتش رو کشیدید SN هست و من نمیدونم چطوری از این SN در متد مجزا استفاده کنم
کد:
button.Click += E => Chart.TryChangeTimeFrameAndSymbol(TimeFrame, SN);
 

the_king

مدیرکل انجمن
ببخشید یک سوال دیگه
من در نرم افزار ctrader که تحت سی شارپ هم هست کاری که button باید انجام بده رو به صورت زیر مینوشتم و کار هم میداد
ولی الان ویژال استودیو این رو قبول نمیکنه
راهی هست بدون اینکه یک متد مجزا بنویسم بتونم رویداد button رو تعریف کنم؟
چون در همون کدی که زحمتش رو کشیدید SN هست و من نمیدونم چطوری از این SN در متد مجزا استفاده کنم
کد:
button.Click += E => Chart.TryChangeTimeFrameAndSymbol(TimeFrame, SN);
فرضا رخداد کلیک دو تا پارامتر sender و e داره :
کد:
        private void Button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show("Hello");
        }
و اینطوری به متد متصل میشه :
کد:
            button1.Click += Button1_Click;
و اگر بخواهیم بجای متد Button1_Click خود کد داخل Button1_Click رو به Click متصل کنیم عبارت لامبدا (Lambda Expressions) اینطوری نوشته میشه :
کد:
            button1.Click += (x, y) => { MessageBox.Show("Hello"); };
اون x و y همون پارامتر های sender و e هستند که بهشون اسامی دیگری دادیم تا تداخلی پیش نیاد.
احیانا اگر از داخل متدی خواستید این عبارت لامبدا رو بنویسید که قبلا x و y رو تعریف کرده بوده، لازمه که نام دیگری در نظر بگیرید.
مثلا در اینجا نمیشه از اسم x استفاده کرد و کامپایلر به نام پارامتر x ایراد خواهد گرفت، باید نام دیگری برای پارامتر اول در نظر گرفت، مثلا z، چون x قبلا تعریف شده :
کد:
            int x = 4;
            button1.Click += (x, y) => { MessageBox.Show("Hello"); };
 

MRHADI

Member
ممنونم
عالی بود عالی بود
من دوباره می پرسم
قطعا جواب ها بعدا به دیگران هم کمک خواهد کرد
من یک ComboBox رو روی فرم خودم درگ کردم
و آیتم هاش رو از نوع TimeFrame (نوعی خاص)قرار دادم
ولی بعد که میخوام از آیتم های ComboBox استفاده کنم تبدیل شدن به آبجکت و خطا میگیره
یعنی داده های من که نوعش TimeFrame بوده الان شده آبجکت
چه راهکاری براش هست؟
نمونه کدی که نوشتم رو میزارم
سپاس
کد:
            _f1.comboBox1.Items.AddRange(new TimeFrame[] { TimeFrame.Monthly,TimeFrame.Weekly,TimeFrame.Daily});
 

the_king

مدیرکل انجمن
ممنونم
عالی بود عالی بود
من دوباره می پرسم
قطعا جواب ها بعدا به دیگران هم کمک خواهد کرد
من یک ComboBox رو روی فرم خودم درگ کردم
و آیتم هاش رو از نوع TimeFrame (نوعی خاص)قرار دادم
ولی بعد که میخوام از آیتم های ComboBox استفاده کنم تبدیل شدن به آبجکت و خطا میگیره
یعنی داده های من که نوعش TimeFrame بوده الان شده آبجکت
چه راهکاری براش هست؟
نمونه کدی که نوشتم رو میزارم
سپاس
کد:
            _f1.comboBox1.Items.AddRange(new TimeFrame[] { TimeFrame.Monthly,TimeFrame.Weekly,TimeFrame.Daily});
دلیل خطا اینه که نوع داده ای رو بکار می برید که نمیتونه بصورت ضمنی به []object تبدیل بشه. یا باید خودتون متد تبدیل رو بنویسید و یا نوع داده ای رو بکار ببرید که این تبدیل بصورت خودکار برایش انجام بشه.
Items.AddRange صرفا []object قبول می کنه، پس فقط برای مقادیری به عنوان پارامتر اجرا میشه که یا خودشون []object هستند یا بصورت ضمنی (implicit) به []object تبدیل شدنی هستند یا کد نویس صریحا تبدیل رو انجام بده.
مثلا ما این کد رو بدون مشکل اجرا می کنیم :
کد:
comboBox1.Items.AddRange(new string[] { "One", "Two", "Three" });
AddRange به این دلیل []string رو قبول می کنه که []string بصورت ضمنی به []object قابل تبدیل شدنه :
کد:
            string[] strings = new string[] {"One", "Two", "Three"};
            object[] objects = strings;
ولی []int بصورت ضمنی به []object تبدیل نمیشه، چون int ها value type هستند، با ارجاع مقدار دهی نمی شوند، int و double و ... مثل string و کلاس ها reference type نیستند :
کد:
            int[] numbers = new int[] {1, 2, 3};
            object[] objects = numbers; // Error: Cannot implicitly convert type int[] to object[]
چه آرایه هایی بصورت ضمنی به []object قابل تبدیل هستند؟ هر نوع آرایه ای که reference type باشه، در واقع نوعی از class باشه. مثلا نباید struct باشه چون struct هم value type ئه.
enum ها هم همینطور، اونها هم value type هستند.
مثلا Color یک struct ئه و بصورت ضمنی به []object تبدیل نمیشه :
کد:
            Color[] colors = new Color[] {Color.Red, Color.Green, Color.Blue};
            object[] objects = colors; // Error: Cannot implicitly convert type Color[] to object[]
ولی مثلا Brush یک class ئه و بدون مشکل بصورت ضمنی به []object تبدیل میشه :
کد:
            Brush[] brushes = new Brush[] { Brushes.Red, Brushes.Green, Brushes.Blue };
            object[] objects = brushes;

من اگر بخواهم یک enum بسازم بصورت ضمنی آرایه اش به []object تبدیل نمیشه :
کد:
        public enum TimeFrame
        {
            Monthly, Weekly, Daily
        }

        TimeFrame[] timeFrames = new TimeFrame[] { TimeFrame.Monthly, TimeFrame.Weekly, TimeFrame.Daily };
        object[] objects = timeFrames; // Error: Cannot implicitly convert type TimeFrame[] to object[]
اما اگر کلاسی با کارکردی شبیه به اون رو میساختم قضیه فرق می کرد :
کد:
        public sealed class TimeFrame
        {
            public static readonly TimeFrame Monthly = new TimeFrame();
            public static readonly TimeFrame Weekly = new TimeFrame();
            public static readonly TimeFrame Daily = new TimeFrame();
        }
البته پیاده سازی یک کلاسی که کاملا شبیه enum عمل کنه نیاز به کد نویسی بیشتری داره.

یا می توانم خودم برای enum ای که میسازم مبدل به []object رو کد نویسی کنم :
کد:
        public static object[] ToObjectArray(TimeFrame[] timeFrames)
        {
            if (timeFrames == null)
            {
                return null;
            }
            var objects = new object[timeFrames.Length];
            timeFrames.CopyTo(objects, 0);
            return objects;
        }
و از اون مبدل استفاده کنم :
کد:
            comboBox1.Items.AddRange(ToObjectArray(new TimeFrame[] { TimeFrame.Monthly, TimeFrame.Weekly, TimeFrame.Daily }));
 

MRHADI

Member
دلیل خطا اینه که نوع داده ای رو بکار می برید که نمیتونه بصورت ضمنی به []object تبدیل بشه. یا باید خودتون متد تبدیل رو بنویسید و یا نوع داده ای رو بکار ببرید که این تبدیل بصورت خودکار برایش انجام بشه.
Items.AddRange صرفا []object قبول می کنه، پس فقط برای مقادیری به عنوان پارامتر اجرا میشه که یا خودشون []object هستند یا بصورت ضمنی (implicit) به []object تبدیل شدنی هستند یا کد نویس صریحا تبدیل رو انجام بده.
مثلا ما این کد رو بدون مشکل اجرا می کنیم :
کد:
comboBox1.Items.AddRange(new string[] { "One", "Two", "Three" });
AddRange به این دلیل []string رو قبول می کنه که []string بصورت ضمنی به []object قابل تبدیل شدنه :
کد:
            string[] strings = new string[] {"One", "Two", "Three"};
            object[] objects = strings;
ولی []int بصورت ضمنی به []object تبدیل نمیشه، چون int ها value type هستند، با ارجاع مقدار دهی نمی شوند، int و double و ... مثل string و کلاس ها reference type نیستند :
کد:
            int[] numbers = new int[] {1, 2, 3};
            object[] objects = numbers; // Error: Cannot implicitly convert type int[] to object[]
چه آرایه هایی بصورت ضمنی به []object قابل تبدیل هستند؟ هر نوع آرایه ای که reference type باشه، در واقع نوعی از class باشه. مثلا نباید struct باشه چون struct هم value type ئه.
enum ها هم همینطور، اونها هم value type هستند.
مثلا Color یک struct ئه و بصورت ضمنی به []object تبدیل نمیشه :
کد:
            Color[] colors = new Color[] {Color.Red, Color.Green, Color.Blue};
            object[] objects = colors; // Error: Cannot implicitly convert type Color[] to object[]
ولی مثلا Brush یک class ئه و بدون مشکل بصورت ضمنی به []object تبدیل میشه :
کد:
            Brush[] brushes = new Brush[] { Brushes.Red, Brushes.Green, Brushes.Blue };
            object[] objects = brushes;

من اگر بخواهم یک enum بسازم بصورت ضمنی آرایه اش به []object تبدیل نمیشه :
کد:
        public enum TimeFrame
        {
            Monthly, Weekly, Daily
        }

        TimeFrame[] timeFrames = new TimeFrame[] { TimeFrame.Monthly, TimeFrame.Weekly, TimeFrame.Daily };
        object[] objects = timeFrames; // Error: Cannot implicitly convert type TimeFrame[] to object[]
اما اگر کلاسی با کارکردی شبیه به اون رو میساختم قضیه فرق می کرد :
کد:
        public sealed class TimeFrame
        {
            public static readonly TimeFrame Monthly = new TimeFrame();
            public static readonly TimeFrame Weekly = new TimeFrame();
            public static readonly TimeFrame Daily = new TimeFrame();
        }
البته پیاده سازی یک کلاسی که کاملا شبیه enum عمل کنه نیاز به کد نویسی بیشتری داره.

یا می توانم خودم برای enum ای که میسازم مبدل به []object رو کد نویسی کنم :
کد:
        public static object[] ToObjectArray(TimeFrame[] timeFrames)
        {
            if (timeFrames == null)
            {
                return null;
            }
            var objects = new object[timeFrames.Length];
            timeFrames.CopyTo(objects, 0);
            return objects;
        }
و از اون مبدل استفاده کنم :
کد:
            comboBox1.Items.AddRange(ToObjectArray(new TimeFrame[] { TimeFrame.Monthly, TimeFrame.Weekly, TimeFrame.Daily }));
ممنون از وقتی که گذاشتید
چه آموزش جامعی داره میشه
احتمالا من نتونستم منظورم رو درست برسونم
من الان این کد رو بدون مشکل و خطا دارم استفاده میکنم و آیتم های ComboBox هم به درستی ساخته میشن
کد:
_f1.comboBox1.Items.AddRange(new TimeFrame[] { TimeFrame.Monthly,TimeFrame.Weekly,TimeFrame.Daily});
الان این درسته و اینا تبدیل شدن به آبجکت
مسئله الان اینه که یه همچین چیزی احتیاج دارم
کد:
            var ttt = _f1.comboBox1.Items;
و میخوام از ttt به عنوان یه مجموعه از تایم فریم ها استفاده کنم اما اون رو آبجکت میشناسه
امیدوارم تونسته باشم برسونم
ساده بگم: من یه چیزی رو دادم به ComboBox و اونم قبولش کرده ولی الان که میخوام از ComboBox خروجی بگیرم باید دوباره تبدیلش کنم به همون تایم فریم ولی آبجکت هست
مشکل دیگه ای هم که دارم اینه با تغییر آیتم های ComboBox در فرم ساخته شده ، کاری انجام نمیشه با اینکه براش تعریف کردم و به اصطلاح فرم رفرش نمیشه|
حتی از همون تایمری هم که فرمودین استفاده کردم ولی نشد
ممنون
 

MRHADI

Member
متوجه شدم مشکل دوم از کجاست(اینکه با تغییر آیتم در ComboBox کاری انجام نمیشد)
من یه همچین کدی نوشتم
کد:
_f1.comboBox1.SelectedIndexChanged += (d, l) => {TimeFrame tf7 = TimeFrame.Daily; };
و نمیتونم از tf7 بیرون از این کد استفاده کنم به همین خاطر کاری انجام نمیشه با انتخاب آیتم ها
چطوری باید عمل کنم
سپاس
 

the_king

مدیرکل انجمن
ممنون از وقتی که گذاشتید
چه آموزش جامعی داره میشه
احتمالا من نتونستم منظورم رو درست برسونم
من الان این کد رو بدون مشکل و خطا دارم استفاده میکنم و آیتم های ComboBox هم به درستی ساخته میشن
کد:
_f1.comboBox1.Items.AddRange(new TimeFrame[] { TimeFrame.Monthly,TimeFrame.Weekly,TimeFrame.Daily});
الان این درسته و اینا تبدیل شدن به آبجکت
مسئله الان اینه که یه همچین چیزی احتیاج دارم
کد:
            var ttt = _f1.comboBox1.Items;
و میخوام از ttt به عنوان یه مجموعه از تایم فریم ها استفاده کنم اما اون رو آبجکت میشناسه
امیدوارم تونسته باشم برسونم
اون comboBox1.Items یک ObjectCollection ئه، یجور List ئه، به همین جهت مستقیما به آرایه تبدیل نمیشه و وقتی هم آرایه میشه object تحویل میده.
اما می توانید در دو مرحله اول با Cast اعضاء اش رو به TimeFrame تبدیل کنید و بعد به آرایه تبدیلش کنید. از نظر ظاهر کد مختصر و ساده است، اما پشت پرده کد زیادی داشته :
کد:
            var ttt = _f1.comboBox1.Items.Cast<TimeFrame>().ToArray();

متوجه شدم مشکل دوم از کجاست(اینکه با تغییر آیتم در ComboBox کاری انجام نمیشد)
من یه همچین کدی نوشتم
کد:
_f1.comboBox1.SelectedIndexChanged += (d, l) => {TimeFrame tf7 = TimeFrame.Daily; };
و نمیتونم از tf7 بیرون از این کد استفاده کنم به همین خاطر کاری انجام نمیشه با انتخاب آیتم ها
چطوری باید عمل کنم
سپاس
SelectedIndexChanged وقتی رخ میده که از بین آیتم های داخل ComboBox یکی شون رو انتخاب می کنید، ComboBox.SelectedItem اون آیتم انتخاب شده است.
d همون comboBox1 ئه و l هم یک Event.Args خالی و بدرد نخور ئه. معمولا در SelectedIndexChanged شما آیتم انتخاب شده رو بررسی می کنید و بر اساسش کاری انجام می دهید.
آیتم انتخاب شده SelectedItem ئه :
کد:
            _f1.comboBox1.SelectedIndexChanged += (d, l) =>
            {
                var tf = (TimeFrame)_f1.comboBox1.SelectedItem;
            };
که از طریق d هم میشه بهش دسترسی داشت چون d همون پارامتر sender ئه :
کد:
            _f1.comboBox1.SelectedIndexChanged += (d, l) =>
            {
                var tf = (TimeFrame)((ComboBox)d).SelectedItem;
            };
حالا ممکنه شما بخواهید tf رو در TextBox یا Label ای نمایش بدید یا بر اساس مقدارش کار خاصی انجام بشه.
اما اگر کار دیگری می خواهید انجام بدید که اطلاع ندارم به وضوح مشخص کنید تا توضیح بدم.
 

MRHADI

Member
_f1.comboBox1.SelectedIndexChanged += (d, l) =>
{
var tf = (TimeFrame)_f1.comboBox1.SelectedItem;
};
نمیدونم چطور تشکر کنم
ممنون
الان من این tf رو چطور میتونم خارج از متد دستور استفادش کنم؟
برام یک سوال هست که قبلا هم دردسر ساز بوده
مثلا فرض کنید من کد زیر رو دارم ، چطور از SN باید خارج از آکولادهای foreach استفاده کنم؟ یا نمونش همین کدی که زحمتش رو کشیدید
کد:
            foreach (var SN in symbolNames)
            {
                //
                //
                //
            }
 

the_king

مدیرکل انجمن
نمیدونم چطور تشکر کنم
ممنون
الان من این tf رو چطور میتونم خارج از متد دستور استفادش کنم؟
برام یک سوال هست که قبلا هم دردسر ساز بوده
مثلا فرض کنید من کد زیر رو دارم ، چطور از SN باید خارج از آکولادهای foreach استفاده کنم؟ یا نمونش همین کدی که زحمتش رو کشیدید
کد:
            foreach (var SN in symbolNames)
            {
                //
                //
                //
            }
باید یک مثال از استفاده اش بزنید. شما می توانید قبل از foreach یا خارج از بدنه متد ای که foreach در اون قرار داره متغیری تعریف کنید و داخل foreach مقدار SN رو با هر شرط خاصی که لازمه داخل اون متغیر کپی کنید.
 

MRHADI

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

the_king

مدیرکل انجمن
هر استفاده ای از SN بخوام داشته باشم فقط داخل حلقه هست دیگه،بیرون از حلقه نمیتونم از SN استفاده کنم و نرم افزار اون رو نمیشناسه
مثلا در همین کد آخری که لطف کردید نوشتید (var tf) ، من از tf ساخته شده در این متد چطور باید بیرون متد استفاده کنم؟وقتی بیرون از متد تایپ میکنم tf ، نرم افزار همچین آیتمی نداره
اگر ممکنه کپی رو که فرمودید با کد مثال بزنید ، مثلا در مورد همین کدی خودتون زحمت کشیدید
ممنونم
متوجه مشکل نمی شوم، برای همین ازتون درخواست مثال برای استفاده کردم. طبعا من ممکنه صد تا مثال برای استفاده بزنم که از نظر من مشکلی ندارند.
مشکل بر اساس استفاده ای مطرح میشه که شما تو ذهن تون دارید و راه حلی به نظرتون نمیرسه.
کد:
        private void Form1_Load(object sender, EventArgs e)
        {
            textBox1.Text = "12,45,27,9";
        }

        private void button1_Click(object sender, EventArgs e)
        {
            var items = textBox1.Text.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            var max = 0;
            foreach (var item in items)
            {
                max = Math.Max(max, int.Parse(item));
            }
            MessageBox.Show(max.ToString());
        }

        private void button2_Click(object sender, EventArgs e)
        {
            var items = textBox1.Text.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            string last = null;
            foreach (var item in items)
            {
                last = item;
            }
            MessageBox.Show(last);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            var items = textBox1.Text.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            string first = null;
            foreach (var item in items)
            {
                if (first == null)
                {
                    first = item;
                }
            }
            MessageBox.Show(first);
        }

        private void button4_Click(object sender, EventArgs e)
        {
            var items = textBox1.Text.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            var sum = 0;
            foreach (var item in items)
            {
                Add(item, ref sum);
            }
            MessageBox.Show(sum.ToString());
        }

        private void Add(string item, ref int sum)
        {
            sum += int.Parse(item);
        }
 

MRHADI

Member
متشکرم
من نمونه کدی رو که به تقلید از شما نوشتم میزارم ، این کد کار نمیده
یه همچین استفاده ای میخوام بکنم
کد:
            TimeFrame nh = null;

           
            _f1.comboBox1.SelectedIndexChanged += (d, l) =>
            {
                var tfst = (TimeFrame)_f1.comboBox1.SelectedItem;
                nh = tfst;
            };
            System.Windows.Forms.MessageBox.Show(nh.ToString());
میخوام از tfst خارج از متد SelectedIndexChanged استفاده کنم
 

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

بالا