C# 窗口表单冻结和数字输出

本文关键字:数字输出 冻结 表单 窗口 | 更新日期: 2023-09-27 18:36:33

我正在用C#做一个项目,我必须制作控制台应用程序和Windows窗体应用程序。我已经完成了控制台应用程序,但现在我在将其"转换"为Windows窗体时遇到了问题。应用程序必须计算来自 infite 系列的欧拉数。我的问题是当我启动它时,它会冻结并且不会在文本框中给我任何输出。我想要的输出当然是数字。这是代码本身。我希望在没有 gui 的情况下理解它不会有问题。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
    namespace Eulerovo_cislo_windows_forms
   {
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    private void button1_Click(object sender, EventArgs e)
    {
        int konst = Int32.Parse(textBox1.Text);
        double euler = 1;
        int variable = 0; 
        int index = 0; 
        int factorial = 0; 
        double absolute_error; 
        variable = konst;
        richTextBox1.Text = ("---ongoing calculations---");
        while (true)
        {
            try
            {
          if (konst < 1 || konst > 30) throw new ArgumentOutOfRangeException("you have not entered a value between 1 and 30");
                while (variable > 0)
                {
                    if (variable == 1) 
                        factorial = 1;
                    else
                    {
                        index = variable - 1;
                        faktorial = variable; 

                        while (index > 0) 
                        {
                            factorial *= index; 
                            index--; 
                        } 
                    }
                    promenna--; 
                    euler += Convert.ToDouble(1) / factorial;  
                    richTextBox1.Multiline = true;
                    richTextBox1.Text = euler.ToString();

                }
            }
            catch (ArgumentOutOfRangeException ae) // exceptions
            {
                MessageBox.Show(ae.Message);
                break;
            }
            catch (FormatException fe)
            {
                MessageBox.Show(fe.Message);
                break;
            }
            finally
            {

                absolute_error = EulerNumberError.error(euler);
                textBox3.Text = absolute_error.ToString();
                textBox2.Text = euler.ToString();

            }
        }
    }
    private void button2_Click(object sender, EventArgs e)
    {
        Application.Exit();
    }

}
class EulerNumberError // absolute error 
        {
        public static double error(double a)
        {
            return Math.E - a;
        }
        }
}

C# 窗口表单冻结和数字输出

需要在单独的线程中调用它,并在 UI 线程中更新文本框。您的 UI 被冻结,因为您占用了主线程,并且永远不会让您的 UI 有机会更新显示。以下是您可以使用后台工作者尝试的内容。

private void button1_Click(object sender, EventArgs e)
{
    int konst = Int32.Parse(textBox1.Text);
    double euler = 1;
    int variable = 0; 
    int index = 0; 
    int factorial = 0; 
    double absolute_error; 
    variable = konst;
    richTextBox1.Text = ("---ongoing calculations---");
    richTextBox1.Multiline = true;
    BackgroundWorker worker = new BackgroundWorker();
    worker.ProgressChanged += (s, e) =>
    {
      string s = e.UserState as string;
      richTextBox1.Text = s;
    };
   worker.RunWorkerCompleted += delegate
   {
      textBox3.Text = absolute_error.ToString();
      textBox2.Text = euler.ToString();   
   };
    worker.DoWork += delegate
    {
    while (true)
    {
        try
        {
      if (konst < 1 || konst > 30) throw new ArgumentOutOfRangeException("you have not entered a value between 1 and 30");
            while (variable > 0)
            {
                if (variable == 1) 
                    factorial = 1;
                else
                {
                    index = variable - 1;
                    faktorial = variable; 

                    while (index > 0) 
                    {
                        factorial *= index; 
                        index--; 
                    } 
                }
                promenna--; 
                euler += Convert.ToDouble(1) / factorial;  
               worker.ReportProgress(0,  euler.ToString());
            }
        }
        catch (ArgumentOutOfRangeException ae) // exceptions
        {
            MessageBox.Show(ae.Message);
            break;
        }
        catch (FormatException fe)
        {
            MessageBox.Show(fe.Message);
            break;
        }
        finally
        {
            absolute_error = EulerNumberError.error(euler);
        }
    }
   }
   worker.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
    Application.Exit();
}

}

因此,您有一个答案,显示了如何使用BackgroundWorker执行此操作。但是,正如 Servy 所建议并且我同意的那样,在当前版本的 .NET 中,最好使用更现代的 async/await 习语来实现这一点。当然,在这样做时,实际利用这个成语很重要。

下面是执行此操作的 Click 事件处理程序的版本:

private async void button1_Click(object sender, EventArgs e)
{
    button1.Enabled = false;
    int konst = Int32.Parse(textBox1.Text);
    double euler = 1;
    int variable = 0;
    int index = 0;
    int factorial = 0;
    double absolute_error;
    variable = konst;
    richTextBox1.Multiline = true;
    richTextBox1.ReadOnly = true;
    richTextBox1.Text = ("---ongoing calculations---");
    while (true)
    {
        try
        {
            if (konst < 1 || konst > 30)
            {
                throw new ArgumentOutOfRangeException("you have not entered a value between 1 and 30");
            }
            while (variable > 0)
            {
                await Task.Run(() =>
                {
                    int iterationMax = 100;
                    while (variable > 0 && iterationMax-- > 0)
                    {
                        if (variable == 1)
                        {
                            factorial = 1;
                        }
                        else
                        {
                            index = variable - 1;
                            factorial = variable;
                            while (index > 0)
                            {
                                factorial *= index;
                                index--;
                            }
                        }
                        //promenna--;
                        euler += 1d / factorial;
                    }
                });
                richTextBox1.Text = euler.ToString();
            }
        }
        catch (ArgumentOutOfRangeException ae) // exceptions
        {
            MessageBox.Show(ae.Message);
            break;
        }
        catch (FormatException fe)
        {
            MessageBox.Show(fe.Message);
            break;
        }
        finally
        {
            absolute_error = EulerNumberError.error(euler);
            textBox3.Text = absolute_error.ToString();
            textBox2.Text = euler.ToString();
        }
    }
    button1.Enabled = true;
}

一些注意事项:

  • 不得不注释掉减少"promenna"的行,我不得不将语句faktorial = variable;更改为factorial = variable;。我没有费心去仔细看这里的数学,所以我真的不知道这些是否是适当的变化。由于发布的代码从未修改variable值,因此算法不可能实际完成,因此我无法知道这些更改是否接近正确。
  • 外部循环的每次迭代
  • 所花费的时间对于每次迭代的 UI 更新来说实在太小了。如果尝试在每次迭代时更新,窗口的消息队列将被填满并导致程序暂时停止响应。因此,我为工作添加了一个中间计数器,以确保它在每次外部迭代上花费足够的时间,以便 UI 线程可以跟上。在我的计算机上,计数 100 足以缓解该问题,但我能够使用高达 10,000,000 的值,同时仍然在 UI 上看到我认为合理的刷新速度。较高的值(尤其是高于 10,000)效果更好。
  • 我添加了在工作进行时禁用按钮的逻辑,以确保不会有用户尝试一次执行多个操作。
  • 此方法中有代码用于初始化实际上应该在 VS 设计器中设置的 RichTextBox 对象的属性。

最后,我完全同意 Servey 的观点,即最好抽象数学运算,使其根本不依赖于 UI。 也就是说,它可以提供一些机制,无论是async的还是基于事件的,以允许 UI 更新,而无需数学代码本身对 UI 一无所知。但是上面应该有效,并说明了在简单场景中使用async/await的正确方法。

button1_Click try块中捕获正常Exception。似乎您有一些例外,但看不到它,因为只处理ArgumentOutOfRangeExceptionFormatException。而且try-catch周围有无限循环...