C# Console.ReadKey粘贴文本时出现意外行为-一次输入多个键

C# Console.ReadKey粘贴文本时出现意外行为-一次输入多个键,c#,.net,console,C#,.net,Console,调用Console.ReadKey()并在提示下粘贴(编辑->粘贴)长度大于1的任何剪贴板文本(比如说“Hello World!”) 我刚刚意外地发现,在随后调用Console.ReadKey()时,没有向用户提供提示,而是没有给出提示!而是读取粘贴文本中的后续字符。。。没有提示 底线:要使用ReadKey,我需要确保(非常确定)下次调用ReadKey时,它实际上会给用户一个提示。目前,这使得ReadKey在这样的关键场景中不可用。 看起来没有任何方法可以禁用此功能,或者只是清除输入但尚未读取的

调用
Console.ReadKey()
并在提示下粘贴(编辑->粘贴)长度大于1的任何剪贴板文本(比如说“Hello World!”)

我刚刚意外地发现,在随后调用Console.ReadKey()时,没有向用户提供提示,而是没有给出提示!而是读取粘贴文本中的后续字符。。。没有提示

底线:要使用ReadKey,我需要确保(非常确定)下次调用ReadKey时,它实际上会给用户一个提示。目前,这使得ReadKey在这样的关键场景中不可用。

看起来没有任何方法可以禁用此功能,或者只是清除输入但尚未读取的值

在我的例子中,这给简单的控制台应用程序带来了严重的错误,我在这里循环查找“RUN”的单词“R”,或者任何“E”到“END”。由于此时我提示输入一个文件路径,因此对于已经使用过它的用户来说,在一次意外事件中粘贴文件路径是相当容易的。问题是(!!!),如果该文件路径包含“r”,它将立即运行

using System;

namespace Practise1
{
    class Program
    {
        static void Main()
        {
            RunDialogue2();
        }

        public static void RunDialogue()
        {
            while (true) {
                Console.WriteLine("Type 'R' to RUN, 'E' to END");
                ConsoleKey key = Console.ReadKey().Key;
                Console.WriteLine("\r\n");

                switch (key) {
                    case ConsoleKey.R:
                        Console.WriteLine("RUN! ENGINES BLAST OFF!");
                        BlastOff();
                        break;
                    case ConsoleKey.E:
                        Console.WriteLine("FINISHED");
                        return;
                    default:
                        Console.WriteLine("INVALID ENTRY, TRY AGAIN");
                        // we *thought* this would pick up bad input, but not pasted text!
                        break;
                }
            }

        }

        public static void RunDialogue2()
        {
            while (true) {
                Console.WriteLine("Type 'R' to RUN, 'E' to END");
                ConsoleKey key = Console.ReadKey().Key;
                Console.WriteLine("\r\n");

                switch (key) {
                    case ConsoleKey.R:
                        Console.WriteLine("RUN! ENGINES BLAST OFF!");
                        Console.WriteLine("We blasted off at: " + DateTime.UtcNow);
                        //BlastOff();
                        break;
                    case ConsoleKey.E:
                        Console.WriteLine("FINISHED");
                        return;
                    default:
                        Console.WriteLine("INVALID ENTRY, TRY AGAIN");
                        // we *thought* this would pick up bad input, but not pasted text!
                        break;
                }
            }
        }
    }
}
如果你有“查理的!”在你的剪贴板上粘贴,这里是输出。注意你是如何获得多次跑步的!都是无意的。非常可怕的是,这是多么糟糕,这对我来说意味着我们永远不能依赖ReadKey作为一种可实现的输入方法,特别是当我们认为我们正在验证输入时:

Type 'R' to RUN, 'E' to END
C

INVALID ENTRY, TRY AGAIN
Type 'R' to RUN, 'E' to END
h

INVALID ENTRY, TRY AGAIN
Type 'R' to RUN, 'E' to END
a

INVALID ENTRY, TRY AGAIN
Type 'R' to RUN, 'E' to END
r

RUN! ENGINES BLAST OFF!
We blasted off at: 7/21/2013 11:24:36 PM
Type 'R' to RUN, 'E' to END
l

INVALID ENTRY, TRY AGAIN
Type 'R' to RUN, 'E' to END
e

FINISHED

我知道一个解决方案是使用Console.ReadLine,但是用一个按键立即进行交互的能力要快乐得多。有什么方法可以避免这种奇怪的行为吗?

类似的方法可以消除readkey的常量读取:

    public static void RunDialogue()
    {
        while (true)
        {
            Console.WriteLine("Type 'R' to RUN, 'E' to END");
            ConsoleKey key = Console.ReadKey().Key;
            Console.WriteLine("\r\n");
            switch (key)
            {
                case ConsoleKey.R:
                    Console.WriteLine("RUN! ENGINES BLAST OFF!");
                    BlastOff();
                    break;
                case ConsoleKey.E:
                    Console.WriteLine("FINISHED");
                    return;
                default:
                    Console.WriteLine("INVALID ENTRY, TRY AGAIN");
                    // we *thought* this would pick up bad input, but not pasted text!
                    break;
            }
            Console.In.ReadToEnd();
        }

    }
虽然这将消除原始帖子中的直接问题,但更完整的解决方案是读取并忽略输入流的其余部分,而不将其回显到屏幕上,类似这样的方法将起作用:

    public static void RunDialogue()
    {
        while (true)
        {
            Console.WriteLine("Type 'R' to RUN, 'E' to END");
            ConsoleKey key = Console.ReadKey().Key;
            Console.WriteLine("\r\n");
            switch (key)
            {
                case ConsoleKey.R:
                    Console.WriteLine("RUN! ENGINES BLAST OFF!");
                    BlastOff();
                    break;
                case ConsoleKey.E:
                    Console.WriteLine("FINISHED");
                    return;
                default:
                    Console.WriteLine("INVALID ENTRY, TRY AGAIN");
                    // we *thought* this would pick up bad input, but not pasted text!
                    break;
            }
            while (Console.KeyAvailable)
            {
                Console.ReadKey(true);
            }
        }

    }

我感谢@tinstaafl的回答,它通过检查
Console.keyavable
揭示了关键解决方案。虽然我绝对公平地将这个答案作为解决方案,但下面是最终的解决方案,它涵盖了一些场景,在这些场景中,您必须完全验证输入实际上只是一个字符

        public static ConsoleKey ReadKeySingle(bool intercept = false, ConsoleKey errorKey = ConsoleKey.Clear)
        {
            ConsoleKey key = Console.ReadKey(intercept).Key;
            while (Console.KeyAvailable) {
                Console.ReadKey(true);
                key = errorKey;
            }
            return key;
        }
然后像这样使用(单行更改):


我不会说用户没有得到提示。打印提示,然后从输入缓冲区执行读取。通常,这是控制台应用程序所希望的行为(它允许您一次粘贴对多个问题的响应),但在您的情况下,如Tinstaaff所示,刷新缓冲区。虽然我会在打印提示之前刷新缓冲区,但谁否决了我的问题?绝对卑鄙。本是对的,因为正如我在回答中的评论中所说的,我测试过了,一切都是正确的correct@BenVoigt“我不会说用户没有收到提示。”—就个人而言,我更希望ReadKey只接受输入中的一个键,而放弃其余的键。但是,嘿,我不会因为人们的意见分歧而投票否决他们。我不在乎它的措辞,只要你不是因为一个小的措辞语义问题而否决这个问题。我也没有否决。事实上,op说他不希望readkey读取其余的输入缓冲区并将其分配给
键,而你的答案中似乎遗漏了这一点。我在这个答案中的两个解决方案都解决了这个问题,但就像我说的,第二个是一个更完整的解决方案。我以为我什么都试过了,但错过了“Console.KeyAvailable”。太棒了。现在,我仍然对Console.ReadKey允许输入多个键感到失望,因为它仍然意味着(在我的示例中)如果它以“R”或“E”开头,那么它们将被视为实际命令,而多个字符应该被拒绝。事实上,这让我知道谁来解决这个问题,请参见下面的内容。您所看到的行为实际上是控制台的默认行为,并且适用于许多应用程序。正如你所看到的,通常有一些简单的方法来改变这种行为。通过这次经历,我确实了解到这是一种(出乎意料的!)行为,至少对我来说是出乎意料的,尽管我想很多其他人都不知道这一点。但这不是StackOverflow的关键所在吗?也就是说,如果你否决了我的问题,那么如果你不能提问又有什么意义呢?我没有否决你的问题,也不知道是谁否决了你的问题。有一件事要考虑的是,当问问题时,可能会有一种模糊的违反预期的行为,当然这总是对个人解释的开放。这是像这样的网站的缺点之一。否决票的产生有很多原因,甚至是个人的愤怒。就个人而言,除非这个人足够成熟和专业,能够指出为什么会有反对票,否则他们不值得考虑甚至思考。
        public static void RunDialogue()
        {
            while (true) {
                Console.WriteLine("Type 'R' to RUN, 'E' to END");

                // KEY! Call ReadKeySingle instead
                ConsoleKey key = ReadKeySingle(); // Console.ReadKey().Key; //!!!

                Console.WriteLine("\r\n");

                switch (key) {
                    case ConsoleKey.R:
                        Console.WriteLine("RUN! ENGINES BLAST OFF!");
                        Console.WriteLine("We blasted off at: " + DateTime.UtcNow);

                        // BlastOff();
                        break;
                    case ConsoleKey.E:
                        Console.WriteLine("FINISHED");
                        return;
                    default:
                        Console.WriteLine("INVALID ENTRY, TRY AGAIN");
                        // we *thought* this would pick up bad input, but not pasted text!
                        break;
                }
            }
        }