我如何继续运行我的控制台应用程序,直到一个键按下(如Esc按下?)

我假设它环绕着一个while循环。我不喜欢ReadKey,因为它阻塞操作并要求一个键,而不是只是继续听按键。

如何做到这一点呢?


当前回答

使用以下代码,您可以在控制台执行过程中监听空格键,并暂停,直到按下另一个键,并选择监听Escape键,以打破主循环。

static ConsoleKeyInfo cki = new ConsoleKeyInfo();

while(true) {
      if (WaitOrBreak()) break;
      //your main code
}

private static bool WaitOrBreak(){
    if (Console.KeyAvailable) cki = Console.ReadKey(true);
    if (cki.Key == ConsoleKey.Spacebar)
    {
        Console.Write("waiting..");
        while (Console.KeyAvailable == false)
        {
            Thread.Sleep(250);Console.Write(".");
        }
        Console.WriteLine();
        Console.ReadKey(true);
        cki = new ConsoleKeyInfo();
    }
    if (cki.Key == ConsoleKey.Escape) return true;
    return false;
}

其他回答

从视频诅咒的。net控制台应用程序在c#由杰森罗伯茨在http://www.pluralsight.com

我们可以这样做,有多个运行过程

  static void Main(string[] args)
    {
        Console.CancelKeyPress += (sender, e) =>
        {

            Console.WriteLine("Exiting...");
            Environment.Exit(0);
        };

        Console.WriteLine("Press ESC to Exit");

        var taskKeys = new Task(ReadKeys);
        var taskProcessFiles = new Task(ProcessFiles);

        taskKeys.Start();
        taskProcessFiles.Start();

        var tasks = new[] { taskKeys };
        Task.WaitAll(tasks);
    }

    private static void ProcessFiles()
    {
        var files = Enumerable.Range(1, 100).Select(n => "File" + n + ".txt");

        var taskBusy = new Task(BusyIndicator);
        taskBusy.Start();

        foreach (var file in files)
        {
            Thread.Sleep(1000);
            Console.WriteLine("Procesing file {0}", file);
        }
    }

    private static void BusyIndicator()
    {
        var busy = new ConsoleBusyIndicator();
        busy.UpdateProgress();
    }

    private static void ReadKeys()
    {
        ConsoleKeyInfo key = new ConsoleKeyInfo();

        while (!Console.KeyAvailable && key.Key != ConsoleKey.Escape)
        {

            key = Console.ReadKey(true);

            switch (key.Key)
            {
                case ConsoleKey.UpArrow:
                    Console.WriteLine("UpArrow was pressed");
                    break;
                case ConsoleKey.DownArrow:
                    Console.WriteLine("DownArrow was pressed");
                    break;

                case ConsoleKey.RightArrow:
                    Console.WriteLine("RightArrow was pressed");
                    break;

                case ConsoleKey.LeftArrow:
                    Console.WriteLine("LeftArrow was pressed");
                    break;

                case ConsoleKey.Escape:
                    break;

                default:
                    if (Console.CapsLock && Console.NumberLock)
                    {
                        Console.WriteLine(key.KeyChar);
                    }
                    break;
            }
        }
    }
}

internal class ConsoleBusyIndicator
{
    int _currentBusySymbol;

    public char[] BusySymbols { get; set; }

    public ConsoleBusyIndicator()
    {
        BusySymbols = new[] { '|', '/', '-', '\\' };
    }
    public void UpdateProgress()
    {
        while (true)
        {
            Thread.Sleep(100);
            var originalX = Console.CursorLeft;
            var originalY = Console.CursorTop;

            Console.Write(BusySymbols[_currentBusySymbol]);

            _currentBusySymbol++;

            if (_currentBusySymbol == BusySymbols.Length)
            {
                _currentBusySymbol = 0;
            }

            Console.SetCursorPosition(originalX, originalY);
        }
    }
Console.WriteLine("Hello");
var key = Console.ReadKey(); 
DateTime start = DateTime.Now;
bool gotKey = Console.KeyAvailable;

while ((DateTime.Now - start).TotalSeconds < 2)                
{
    if (key.Key == ConsoleKey.Escape)
    {
       Environment.Exit(0);
    } 
    else if (key.Key == ConsoleKey.Enter)
    {
       break;
    } 

使用控制台。只有当你知道ReadKey不会被阻塞时,你才会调用ReadKey:

Console.WriteLine("Press ESC to stop");
do {
    while (! Console.KeyAvailable) {
        // Do something
   }       
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);

处理一些其他答案不能很好处理的情况:

响应式:显式/直接执行按键处理代码;避免轮询或阻塞延迟的变化 可选性:全局按键是可选的;默认情况下,应用程序正常退出(如果没有操作) 关注点分离:侵入性较小的监听代码;独立于控制台应用程序的核心逻辑运行。


本页上的许多解决方案都涉及轮询控制台。KeyAvailable或在Console.ReadKey上阻塞。虽然。net控制台在这里确实不是很合作,但您可以使用Task。运行以转向更现代的异步侦听模式。

需要注意的主要问题是,默认情况下,你的控制台线程没有设置为Async操作——这意味着,当你从main函数的底部掉出来时,而不是等待Async完成,你的AppDoman和进程将结束。解决这个问题的合适方法是使用Stephen Cleary的AsyncContext在单线程控制台程序中建立完全的Async支持。但对于更简单的情况,比如等待按键,安装一个完整的蹦床可能有点过头了。

下面的例子是用于某种迭代批处理文件的控制台程序。在这种情况下,当程序完成它的工作时,通常它应该退出而不需要按键,然后我们允许一个可选的按键来阻止应用程序退出。我们可以暂停循环来检查内容,也可以继续,或者将暂停作为一个已知的“控制点”,在这个控制点上干净地跳出批处理文件。

static void Main(String[] args)
{
    Console.WriteLine("Press any key to prevent exit...");
    var tHold = Task.Run(() => Console.ReadKey(true));

    // ... do your console app activity ...

    if (tHold.IsCompleted)
    {
#if false   // For the 'hold' state, you can simply halt forever...
        Console.WriteLine("Holding.");
        Thread.Sleep(Timeout.Infinite);
#else                       // ...or allow continuing on (to exit)
        while (Console.KeyAvailable)
            Console.ReadKey(true);     // flush/consume any extras
        Console.WriteLine("Holding. Press 'Esc' to exit.");
        while (Console.ReadKey(true).Key != ConsoleKey.Escape)
            ;
#endif
    }
}

你可以稍微改变一下你的方法——使用Console.ReadKey()来停止你的应用程序,但在后台线程中完成你的工作:

static void Main(string[] args)
{
    var myWorker = new MyWorker();
    myWorker.DoStuff();
    Console.WriteLine("Press any key to stop...");
    Console.ReadKey();
}

在myWorker.DoStuff()函数中,您可以在后台线程上调用另一个函数(使用Action<>()或Func<>()是一种简单的方法),然后立即返回。