C# |控制台应用 |如何让程序在执行下一行之前等待(时间以毫秒为单位)?

C# | Console app | How to make the program wait (time in ms) before executing the next line?

我正在构建一个 c# 控制台应用程序(.NET 框架),我想通过使用一些 "animations" 来制作一个漂亮的应用程序。 我想打印 "Press any Key to continue ..." 并让它闪烁(出现然后消失,直到用户实际按下任意键。

 do
    {
       while (!Console.KeyAvailable)
         {
             Console.WriteLine("Press any key to continue...");

     /* here i want the program to wait for like 500ms,
      clear console and wait 500ms again before rewriting line*/

             Console.Clear();
         }
     } while (Console.ReadKey(true).Key != ConsoleKey.Escape);

一个简单的方法是使用

System.Threading.Thread.Sleep(5000); //暂停 5 秒。所以要闪光灯这样做。

Console.Clear();
Console.WriteLine("SomeText");
System.Threading.Thread.Sleep(1000);
Console.Clear();
Console.WriteLine("SomeText");
System.Threading.Thread.Sleep(1000);
/// ETC.. 

Console.WriteLine("Press Any key To Continue...)

这是一种简单的方法,有助于您理解编码。如果你想让它继续闪烁,那么只需将它放在一个循环中。然而!请记住来自 运行 的有效 "Pauses" 代码。因此,如果它位于暂停行,它将不允许用户按键继续。这就是我将最后一个 Console.WriteLine(); 放在底部的原因。如果您希望用户能够随时按下一个键并使其不断闪烁,那么您将不得不参与多线程,这可能比您感兴趣的要复杂一些。

首先,我不会使用Thread.Sleep。休眠您的主线程会提供非常粗糙的用户体验...这是一种快速而肮脏的方法,但并不是该方法的真正用途。

这是一个使用计时器来闪烁一行文本的简单示例。

using System.Timers;
class Program
{
    static void Main(string[] aszArgs)
    {
        Timer myTimer = new Timer(500);
        myTimer.Elapsed += MyTimer_Elapsed;
        myTimer.Enabled = true;

        //Wait for a key. Or do other work... whatever you want
        Console.ReadKey();
    }

    private static bool cleared = true;
    private static void MyTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        if (cleared)
            Console.WriteLine("Flash text");
        else
            Console.Clear();

        cleared = !cleared;
    }
}  

我在这个例子中使用 System.Timers.Timer,它有一些可靠的文档和示例 here

您可以像这样以相等的间隔执行一批操作:

using System;
using System.Threading;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            new Program().Run();
        }

        private void Run()
        {
            do
            {
                while (!Console.KeyAvailable)
                    this.InvokeWithIntervals(1000,
                                             () => Console.WriteLine("Press any key to continue..."),
                                             () => Console.Clear());
            } while (Console.ReadKey(true).Key != ConsoleKey.Escape);
        }

        private void InvokeWithIntervals(int interval, params Action[] actions)
        {
            foreach(var action in actions)
            {
                action.Invoke();
                Thread.Sleep(interval);
            }
        }
    }
}

一种略有不同的方法是使用 Stopwatch class 来测量时间,并且只有在我们通过指定的 "flash interval".[=22= 时才更改文本]

我们可以编写一个方法来执行此操作,该方法需要一个 string prompt 来显示,以及一个 TimeSpan interval 来指定在闪烁文本之间等待多长时间。

在代码中,我们将捕获光标位置和控制台颜色,启动秒表,然后每当秒表经过 interval 指定的时间量时,我们将交换 Console.ForegroundColorConsole.BackgroundColor.

该方法将执行此操作,直到用户按下一个键,我们将 return 返回给调用者:

private static ConsoleKey FlashPrompt(string prompt, TimeSpan interval)
{
    // Capture the cursor position and console colors
    var cursorTop = Console.CursorTop;
    var colorOne = Console.ForegroundColor;
    var colorTwo = Console.BackgroundColor;

    // Use a stopwatch to measure time interval
    var stopwach = Stopwatch.StartNew();
    var lastValue = TimeSpan.Zero;

    // Write the initial prompt
    Console.Write(prompt);

    while (!Console.KeyAvailable)
    {
        var currentValue = stopwach.Elapsed;

        // Only update text with new color if it's time to change the color
        if (currentValue - lastValue < interval) continue;

        // Capture the current value, swap the colors, and re-write our prompt
        lastValue = currentValue;
        Console.ForegroundColor = Console.ForegroundColor == colorOne 
            ? colorTwo : colorOne;
        Console.BackgroundColor = Console.BackgroundColor == colorOne 
            ? colorTwo : colorOne;
        Console.SetCursorPosition(0, cursorTop);
        Console.Write(prompt);
    }

    // Reset colors to where they were when this method was called
    Console.ForegroundColor = colorOne;
    Console.BackgroundColor = colorTwo;

    return Console.ReadKey(true).Key;
}

现在,在调用方,我们将向它传递文本 "Press escape to continue" 和我们要等待的时间(在您的情况下为 TimeSpan.FromMilliseconds(500)),然后我们可以在无限 while 循环,直到用户按下 ConsoleKey.Escape:

private static void Main()
{
    // Flash prompt until user presses escape
    while (FlashPrompt("Press escape to continue...", 
        TimeSpan.FromMilliseconds(500)) != ConsoleKey.Escape) ;

    // Code execution continues after they press escape...
}

这里的好处是您可以重复使用逻辑并可以指定更短或更长的闪烁时间。您还可以通过在调用方法之前指定它们来更改获得 "flashed" 的颜色(或者可以编写方法以将它们作为参数)。

例如,试试这个:

private static void Main()
{
    Console.WriteLine("Hello! The text below will flash red " + 
        "and green once per second until you press [Enter]");

    Console.ForegroundColor = ConsoleColor.Red;
    Console.BackgroundColor = ConsoleColor.Green;

    while (FlashPrompt("Press [Enter] to continue...", 
        TimeSpan.FromSeconds(1)) != ConsoleKey.Enter) ;

    Console.ResetColor();

    // Code will now continue in the original colors
}

我改进了 ,这导致 CPU 使用率很高。我的回答让 CPU 在等待期间闲置,但需要添加对 System.Windows.Forms.

的引用
private static void Timer_Tick (object sender, EventArgs e)
{
    ((System.Windows.Forms.Timer)sender).Stop();
}
private static ConsoleKey FlashPrompt(string prompt, TimeSpan interval)
{
    // Capture the cursor position and console colors
    var cursorTop = Console.CursorTop;
    var colorOne = Console.ForegroundColor;
    var colorTwo = Console.BackgroundColor;
    
    // Write the initial prompt
    Console.Write(prompt);

    // Set up timer
    System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
    timer.Tick += Timer_Tick;
    timer.Interval = (int)interval.TotalMilliseconds;
    while (!Console.KeyAvailable)
    {
        // Wait the required interval, checking every 100ms
        timer.Start();
        while (!Console.KeyAvailable && timer.Enabled)
        {
            Application.DoEvents();
            System.Threading.Thread.Sleep(100);
        }

        // Wwap the colors, and re-write our prompt
        Console.ForegroundColor = Console.ForegroundColor == colorOne 
            ? colorTwo : colorOne;
        Console.BackgroundColor = Console.BackgroundColor == colorOne 
            ? colorTwo : colorOne;
        Console.SetCursorPosition(0, cursorTop);
        Console.Write(prompt);
    }

    // Reset colors to where they were when this method was called
    Console.ForegroundColor = colorOne;
    Console.BackgroundColor = colorTwo;

    return Console.ReadKey(true).Key;
}