带有循环的C#乘法表程序一直处于冻结状态,两个事件不起作用

本文关键字:状态 冻结 不起作用 事件 两个 于冻结 循环 一直 程序 | 更新日期: 2023-09-27 18:21:48

我一直在C#/Visual Studio中开发这个简单的乘法表程序,它一直给我带来麻烦,因为循环(尤其是do-while)对我来说非常困难。我应该使用嵌套的for循环和嵌套的do-whil循环,尽管我没有得到任何错误,但当我调试或运行程序并单击while loop按钮时,它冻结了,需要停止;forloop按钮仅在单击时显示"0",但dowhile循环按钮似乎工作正常。

我相信这些是有问题的部分-对于循环:

  for (r = 1; r < 10; r++)
        {
            for (c = 1; c < 10; c++)
            {
                intResult = r * c;
                if (intResult < 10)
                    strSpace = "  ";  //two spaces 
                else
                    strSpace = " ";   //one space
                txtTable.AppendText(strSpace); // insert space
                txtTable.AppendText(intResult.ToString());  //insert result
                txtTable.AppendText("'r'n");  //Move down one line
            }
        }
    }

While loop:

  private void btnWhileLoop_Click(object sender, EventArgs e)
    {
        int r = 0; //row
        int c = 0; //column
        int intResult;
        string strSpace;
        txtTable.Clear();    //clear the text box
        txtTable.Refresh();  //refresh the form before exiting the method
        Thread.Sleep(1000);  //wait one second to see the clear text box
        //Outer loop goes down the rows
         r = 1; //initialize r
        do
        {
           //Inner loop goes across the columns
            c = 1; //initialize c
            do
            {

               intResult = r * c;
            } while (r < 10);
           } while (c < 10);
                strSpace = "  ";
                txtTable.AppendText(strSpace); // insert space
               txtTable.AppendText(intResult.ToString());  //insert result
                c++;  //increment c
        {
            txtTable.AppendText("'r'n");  //Move down one line
            r++;  //increment r
        }
    }

完整参考代码:

    namespace CS10b
{
    public partial class frmCS10b : Form
    {
        public frmCS10b()
        {
            InitializeComponent();
        }

        private void btnWhileLoop_Click(object sender, EventArgs e)
        {
            int r = 0; //row
            int c = 0; //column
            int intResult;
            string strSpace;
            txtTable.Clear();    //clear the text box
            txtTable.Refresh();  //refresh the form before exiting the method
            Thread.Sleep(1000);  //wait one second to see the clear text box
            //Outer loop goes down the rows
             r = 1; //initialize r
            do
            {
               //Inner loop goes across the columns
                c = 1; //initialize c
                do
                {

                   intResult = r * c;
                } while (r < 10);
               } while (c < 10);
                    strSpace = "  ";
                    txtTable.AppendText(strSpace); // insert space
                   txtTable.AppendText(intResult.ToString());  //insert result
                    c++;  //increment c
            {
                txtTable.AppendText("'r'n");  //Move down one line
                r++;  //increment r
            }
        }

        //Modify the nested while loops used above to nested do-while loops
        private void btnDoWhileLoop_Click(object sender, EventArgs e)
        {
            int r = 0; //row
            int c = 0; //column
            int intResult;
            string strSpace;
            txtTable.Clear();    //clear the text box
            txtTable.Refresh();  //refresh the form before exiting the method
            Thread.Sleep(1000);  //wait one second to see the clear text box
            txtTable.AppendText("'r'n");
            //Outer loop goes down the rows
            //initialize r
            //do
            //Inner loop goes across the columns
            //initialize c
            //do
            for (r = 1; r < 10; r++)
            {
                for (c = 1; c < 10; c++)
                {
                    intResult = r * c;
                    if (intResult < 10)
                        strSpace = "  ";  //two spaces 
                    else
                        strSpace = " ";   //one space
                    txtTable.AppendText(strSpace); // insert space
                    txtTable.AppendText(intResult.ToString());  //insert result
                    txtTable.AppendText("'r'n");  //Move down one line
                }
            }
        }

        //Modify the nested while loops used above to nested for loops
        private void btnForLoop_Click(object sender, EventArgs e)
        {
            int r = 0; //row
            int c = 0; //column
            int intResult;
            string strSpace;
            txtTable.Clear();    //clear the text box
            txtTable.Refresh();  //refresh the form before exiting the method
            Thread.Sleep(1000);  //wait one second to see the clear text box
            txtTable.AppendText("'r'n"); 

            //Outer loop goes down the rows

            //for (initialize r; Boolean Condition, increment r)
             {
                //Inner loop goes across the columns
                //for (initialize c; Boolean Condition, increment c)
                {
                    intResult = r * c;
                    if (intResult < 10)
                        strSpace = "  ";  //two spaces 
                    else
                        strSpace = " ";   //one space
                    txtTable.AppendText(strSpace); // insert space
                    txtTable.AppendText(intResult.ToString());  //insert result
                }
                txtTable.AppendText("'r'n");  //Move down one line
            }
        }

        private void btnExit_Click(object sender, EventArgs e)
        {
            this.Close();
        }

}
}//end of form
//end of namespace

谢谢。

带有循环的C#乘法表程序一直处于冻结状态,两个事件不起作用

您有两个不同的问题,但都是由同一个根本错误引起的:您在处理事情时没有让UI线程更新。

顺便说一句:您的代码非常混乱。有些事件处理程序的名称似乎与实际操作不匹配。例如,do/while示例在一个名为btnWhileLoop_Click()的方法中,而嵌套的for循环示例则在一个称为btnDoWhileLoop_Click()的方法中。然后有一个名为btnForLoop_Click()的方法,它根本没有任何循环。为了这个答案的目的,我将忽略方法名称,至少因为它们与特定的实现有关,并且只使用它们来识别每个方法。

无论如何…

do/while示例中,主要问题是您没有更改控制循环的变量。由于它永远不会改变,因此允许循环终止的表达式永远不会计算为false,因此循环将永远运行。

在同样的方法中,你有一堆代码,看起来你一开始可能写得很正确,但后来你的弟弟来了,把你的程序从桌子上敲了下来,所以当你把这些片段捡回来时,你只需要按随机顺序把它们放回方法中。也就是说,似乎所有正确的部分都在那里,但它们没有任何连贯的顺序。

下面是一个应该更有效的方法版本:

    private void btnWhileLoop_Click(object sender, EventArgs e)
    {
        int r = 0; //row
        int c = 0; //column
        int intResult;
        string strSpace;
        txtTable.Clear();    //clear the text box
        txtTable.Refresh();  //refresh the form before exiting the method
        Thread.Sleep(1000);  //wait one second to see the clear text box
        //Outer loop goes down the rows
         r = 1; //initialize r
        do
        {
            //Inner loop goes across the columns
            c = 1; //initialize c
            do
            {
                intResult = r * c;
                c++;  //increment c
                txtTable.AppendText(" "); // insert space
                txtTable.AppendText(intResult.ToString());  //insert result
            } while (c < 10);
            txtTable.AppendText("'r'n");  //Move down one line
            r++;  //increment r
        } while (r < 10);
    }

以上仍然存在这样的问题,即在完成整个操作之前,屏幕上不会发生任何变化。但至少现在整个事情都有可能完成我将使用嵌套的for循环示例来展示如何解决第二个问题,即在完成整个任务之前,屏幕上不会显示任何内容。

有多种方法可以解决这个问题,但IMHO最常用、最自然的方法是在后台任务中运行处理,并让UI线程同时工作。这可能看起来像这样:

    private async void btnDoWhileLoop_Click(object sender, EventArgs e)
    {
        int r = 0; //row
        int c = 0; //column
        int intResult;
        string strSpace;
        txtTable.Clear();       //clear the text box
        await Task.Delay(1000); //wait one second to see the clear text box
        txtTable.AppendText("'r'n");
        // set up a helper to make it easy to update the UI from the background
        // task Non-standard event arg type of "string", because we don't need
        // anything fancier
        Progress<string> progress = new Progress<string>();
        // This event is raised when progress.Report() is called (see below)
        progress.ProgressChanged += (sender, e) =>
        {
            txtTable.AppendText(e);
        };
        // Wrap the loops in an anonymous method (i.e. the () => { ... } syntax)
        // and pass that to Task.Run() so it can run as a background task
        await Task.Run(() =>
        {
        //Outer loop goes down the rows
        //initialize r
        //do
        //Inner loop goes across the columns
        //initialize c
        //do
        for (r = 1; r < 10; r++)
        {
            for (c = 1; c < 10; c++)
            {
                intResult = r * c;
                if (intResult < 10)
                    strSpace = "  ";  //two spaces 
                else
                    strSpace = " ";   //one space
                // While the task is running in a thread other than your original
                // UI thread, using the "progress" object here allows the task to pass
                // data back to the UI thread where it is allowed to call e.g.
                // txtTable.AppendText() to update the UI.
                progress.Report(strSpace); // insert space
                progress.Report(intResult.ToString());  //insert result
            }
            // I think you wanted this line outside the inner loop
            progress.Report("'r'n");  //Move down one line
        }
        });
        // There's nothing else to do. But if you wanted to you could add code here
        // to do something after the task is run. Using "await" allows the UI thread
        // to keep running while the task is running, and then return control to
        // this method here when it's done.
    }

注意:

  • 您可能不熟悉第二个示例中的任务、线程、进度、异步等内容。不幸的是,当您真的只是想了解循环时,您选择了在GUI API(即Winforms)中实现代码。这样的API通常有关于如何更新UI的重要而严格的规则,对于您正在编写的代码类型,遵守这些规则的最佳方式是如上所述编写代码。IMHO,如果您在控制台程序中实现循环示例,输出是即时的,那么对您来说会更好、更容易。我希望以上内容不会让你太迷失方向
  • async关键字用于标记使用await关键字的方法。您可以在其他地方阅读有关如何使用这些功能的详细信息;简短的版本是使用CCD_ 13允许该方法实际暂时返回给调用者,这样调用该方法的线程可以在该方法本身等待发生某些事情时继续运行。当发生这种情况时,该方法将在await语句或表达式之后继续执行。这是允许UI线程在其他任务在不同线程中运行时继续工作的关键技术
  • 在大多数GUI API中,包括Winforms、WPF、Winrt、Java等,都有一个必须访问UI对象的"UI线程"。如果您编写的代码在其他线程中执行,并且该代码需要以某种方式与UI交互或更新UI,那么有特定于API的技术可以实现这一点。在.NET中,最简单的机制之一是使用Progress<T>类。这个类隐藏了所需的所有血腥细节(例如参见Control.Invoke()方法),并使数据以直接的方式传递回UI线程变得容易
  • 以上所有操作的最终效果是,在运行循环时,UI线程不会被阻塞。相反,它们可以在不同的线程中运行。这样,当他们(通过progress对象)访问UI时,UI可以通过重新绘制自己并在任务仍在运行时在屏幕上显示新数据来响应您的更新
  • 所有这些都将很快发生。您可能希望将Thread.Sleep(100)语句放入内部循环,这样您就可以看到事情在实际进行时发生的情况。只有100次迭代,如果没有这一点,即使UI线程被取消阻止,仍然在处理结束之前不会更新
  • 您可以将与第二个示例中完全相同的技术应用于第一个示例(do/while示例),这样它也可以在循环实际运行时更新UI