Я пытаюсь нажать Q, чтобы выйти из окна консоли. Мне не нравится моя текущая реализация. Есть ли способ асинхронизировать или использовать обратный вызов для получения ключей с консоли?
Консоль ReadKey асинхронная или обратный вызов?
Ответы (6)
Вы можете вызвать Console.ReadKey()
из другого потока, чтобы он не блокировал ваш основной поток. (Вы можете использовать .Net 4 Task
или старый Thread
, чтобы начать новый поток.)
class Program
{
static volatile bool exit = false;
static void Main()
{
Task.Factory.StartNew(() =>
{
while (Console.ReadKey().Key != ConsoleKey.Q) ;
exit = true;
});
while (!exit)
{
// Do stuff
}
}
}
person
svick
schedule
01.08.2010
На самом деле это то, что я делаю (ну, часть readkey в потоке сравнения). Эта фабрика задач выглядит круто. Проблема с моим кодом прямо сейчас заключается в том, что .ReadKey, кажется, не возвращается, пока я не переведу окно в фокус. Поэтому мне не нравится мое решение. Когда основной поток заканчивается, другой поток все еще заблокирован. Ваше решение выглядит круто, возможно, потоки задач умрут, когда основной. В этом случае это должно работать, однако банкомат. Это приложение только для версии 3.5.
- person ; 01.08.2010
Если вы хотите, чтобы
Thread
останавливался после завершения основного потока, просто установите Thread.IsBackground
в true
.
- person svick; 01.08.2010
И приведенный выше код, и IsBackground решают мою проблему. +1
- person ; 01.08.2010
С подходом задачи вы можете создать новый
CancellationTokenSource
, передать свойство Token
любым задачам, которые вы хотите убить, а затем вызвать CancellationTokenSource.Cancel()
, чтобы убить их. Я уверен, что все знают об этом спустя 8 лет, лол.
- person Seth; 09.01.2019
Я не нашел ни один из существующих ответов полностью удовлетворительным, поэтому я написал свой собственный, чтобы работать с TAP и .Net 4.5.
/// <summary>
/// Obtains the next character or function key pressed by the user
/// asynchronously. The pressed key is displayed in the console window.
/// </summary>
/// <param name="cancellationToken">
/// The cancellation token that can be used to cancel the read.
/// </param>
/// <param name="responsiveness">
/// The number of milliseconds to wait between polling the
/// <see cref="Console.KeyAvailable"/> property.
/// </param>
/// <returns>Information describing what key was pressed.</returns>
/// <exception cref="TaskCanceledException">
/// Thrown when the read is cancelled by the user input (Ctrl+C etc.)
/// or when cancellation is signalled via
/// the passed <paramred name="cancellationToken"/>.
/// </exception>
public static async Task<ConsoleKeyInfo> ReadKeyAsync(
CancellationToken cancellationToken,
int responsiveness = 100)
{
var cancelPressed = false;
var cancelWatcher = new ConsoleCancelEventHandler(
(sender, args) => { cancelPressed = true; });
Console.CancelKeyPress += cancelWatcher;
try
{
while (!cancelPressed && !cancellationToken.IsCancellationRequested)
{
if (Console.KeyAvailable)
{
return Console.ReadKey();
}
await Task.Delay(
responsiveness,
cancellationToken);
}
if (cancelPressed)
{
throw new TaskCanceledException(
"Readkey canceled by user input.");
}
throw new TaskCanceledException();
}
finally
{
Console.CancelKeyPress -= cancelWatcher;
}
}
person
Jodrell
schedule
13.05.2014
Вы можете использовать свойство KeyAvailable (Framework 2.0):
if (System.Console.KeyAvailable)
{
ConsoleKeyInfo key = System.Console.ReadKey(true);//true don't print char on console
if (key.Key == ConsoleKey.Q)
{
//Do something
}
}
person
user2794831
schedule
19.09.2013
Вот как я это сделал:
// Comments language: pt-BR
// Aguarda key no console
private static async Task<ConsoleKey> WaitConsoleKey ( ) {
try {
// Prepara retorno
ConsoleKey key = default;
// Aguarda uma tecla ser pressionada
await Task.Run ( ( ) => key = Console.ReadKey ( true ).Key );
// Retorna a tecla
return key;
}
catch ( Exception ex ) {
throw ex;
}
}
person
Rechdan
schedule
25.06.2018
Хороший. Это лучшее решение на странице с точки зрения точного соблюдения современной
async
чувствительности. Например, важно отметить, что Task.Run(…)
обладает специальными встроенными знаниями о современных облегченных async
методах, сгенерированных компилятором, что позволяет отличать их от устаревших ThreadPool
- или TaskContinuationSource
-поддерживаемых Task
-методов, возвращающих Task
. Это различие важно для поддержания жизни Console
приложений; менее сложный код преждевременно завершает работу по (мнимому) завершению продолжения Task
, возвращаемого методом async
верхнего уровня.
- person Glenn Slayden; 28.09.2019
Почему попытка поймать с повторным вызовом того же исключения?
- person John Hamm; 12.05.2020
@JohnHamm это потому, что в то время у меня там был журнал, поэтому я вынул его и заменил этим броском, но это полностью устранимо. ????
- person Rechdan; 05.07.2020
Исходя из всех ответов здесь, это моя версия:
public class KeyHandler
{
public event EventHandler KeyEvent;
public void WaitForExit()
{
bool exit = false;
do
{
var key = Console.ReadKey(true); //blocks until key event
switch (key.Key)
{
case ConsoleKey.Q:
exit = true;
break;
case ConsoleKey.T:
// raise a custom event eg: Increase throttle
break;
}
}
while (!exit);
}
}
static void Main(string[] args)
{
var worker = new MyEventDrivenClassThatDoesCoolStuffByItself();
worker.Start();
var keyHandler = new KeyHandler();
keyHandler.KeyEvent+= keyHandler_KeyEvent; // modify properties of your worker
keyHandler.WaitForExit();
}
- Он не требует, чтобы Main выполнял какие-либо действия в цикле, позволяя ему просто координировать обработку ключей и манипулирование свойствами рабочего класса.
- Принимая подсказку от @Hans, KeyHandler не нужно асинхронизировать новый поток, поскольку Console.ReadKey блокируется до тех пор, пока не будет получен ключ.
person
fiat
schedule
14.09.2014
Вот реализация, которую я создал с помощью KeyAvailable
. При этом подсказка остается в нижней части окна консоли, в то время как все, что «выводится» на консоль, начинается сверху.
public class Program
{
private static int consoleLine;
private static int consolePromptLine;
private static bool exit;
static string clearLine = new string(' ', Console.BufferWidth - 1);
public static void Main(string[] args)
{
StringBuilder commandCapture = new StringBuilder(10);
string promptArea = "Command> ";
consolePromptLine = Console.WindowTop + Console.WindowHeight - 1;
ClearLine(consolePromptLine);
Console.Write(promptArea);
while (!exit)
{
// Do other stuff
// Process input
if (Console.KeyAvailable)
{
var character = Console.ReadKey(true);
if (character.Key == ConsoleKey.Enter)
{
if (commandCapture.Length != 0)
{
ProcessCommand(commandCapture.ToString());
commandCapture.Clear();
ClearLine(consolePromptLine);
Console.Write(promptArea);
}
}
else
{
if (character.Key == ConsoleKey.Backspace)
{
if (commandCapture.Length != 0)
{
commandCapture.Remove(commandCapture.Length - 1, 1);
ClearLine(consolePromptLine);
Console.Write(promptArea);
Console.Write(commandCapture.ToString());
}
}
else
{
commandCapture.Append(character.KeyChar);
Console.SetCursorPosition(0, consolePromptLine);
Console.Write(promptArea);
Console.Write(commandCapture.ToString());
}
}
}
}
}
private static void ProcessCommand(string command)
{
if (command == "start")
{
Task<string> testTask = new Task<string>(() => { System.Threading.Thread.Sleep(4000); return "Test Complete"; });
testTask.ContinueWith((t) => { Print(t.Result); }, TaskContinuationOptions.ExecuteSynchronously);
testTask.Start();
}
else if (command == "quit")
{
exit = true;
}
Print(command);
consolePromptLine = Console.WindowTop + Console.WindowHeight - 1;
}
public static void Print(string text)
{
ClearLine(consoleLine);
Console.WriteLine(text);
consoleLine = Console.CursorTop;
}
public static void ClearLine(int line)
{
Console.SetCursorPosition(0, line);
Console.Write(clearLine);
Console.SetCursorPosition(0, line);
}
}
person
Thraka
schedule
30.07.2015