简体   繁体   English

在任何输入(C#)期间随时退出控制台应用程序

[英]Exit Console App at any Time During Any Input (C#)

I have a relatively large console app with multiple menu's and user inputs. 我有一个相对较大的控制台应用程序,具有多个菜单和用户输入。 I need to create a way for a user to "Quit" or "Back" at any time essentially break; 我需要为用户创建一种方法,使用户可以在任何时候基本退出break;或退出break; the current method in progress. 当前进行中的方法。 I considered using a whole bunch of conditionals but that would take some time and wouldn't be very clean. 我考虑过使用一堆条件,但这会花费一些时间,而且不是很干净。 Is there any way to continually check for "Q" to quit and run a method based on that input across an entire project? 有什么方法可以持续检查“ Q”是否退出并在整个项目中基于该输入运行方法?

PseudoCode 伪代码


What I have now: 我现在所拥有的:

UserInput #1;
UserInput #2;
UserInput #3; //ETC....
PromptFor Quit; //Option to quit after inputs are completed.

What I thought about trying: 我对尝试的想法:

UserInput #1;
PromptFor Quit#1; //Add prompt to quit after every input as a conditional.
UserInput #2; 
PromptFor Quit#2;
UserInput #3;
PromptFor Quit#3;

What I'd like to have: 我想要的是:

PromptForQuit    //Some method of constantly checking if userInput hit's "Q" or "ESC" key is entered.
    {
      UserInput#1;
      UserInput#2;
      UserInput#3; // etc..
    }

I could solve it by hard-coding it into every single method but there has to be a better way to do it. 我可以通过将其硬编码到每种方法中来解决它,但是必须有一种更好的方法来实现。 Also, I need to output to the console that there is an option to "Q" to quit for every input. 另外,我需要向控制台输出每个输入都可以退出“ Q”的选项。

I haven't done a complex console application since the early 90s. 自90年代初以来,我还没有做过复杂的控制台应用程序。 If I have an app with "with multiple menu's and user inputs", I usually use something (Windows Forms, WPF, a web app) that supports that natively. 如果我有一个带有“具有多个菜单和用户输入”的应用程序,那么我通常会使用某些本身支持该功能的东西(Windows窗体,WPF,Web应用程序)。 But... 但...

If this is a big/complex enough project, and particularly if you have plans to write more than one of these, it might be worth writing a little framework based on the Model-View-Controller (MVC) pattern. 如果这是一个足够大/复杂的项目,尤其是如果您打算编写其中一个以上的计划,那么可能有必要编写一个基于“模型-视图-控制器(MVC)”模式的小框架。

In this case, we'll actually have two models, one that's reasonably complex that describes the program flow, and a second one that is a simple Dictionary that contains the user's answers. 在这种情况下,我们实际上有两个模型,一个模型相当复杂,描述了程序流程,另一个模型是包含用户答案的​​简单Dictionary The Controller is a simple processing loop that executes the directives in the first model. 控制器是一个简单的处理循环,它在第一个模型中执行指令。 The View is very simple, but we'll see that by segregating it out, there are some advantages. 视图非常简单,但是通过将其分离出来,可以看到一些优点。

To do this, you will need to completely change the structure of your programming. 为此,您将需要完全更改编程的结构。 I'm assuming it mostly looks like this: 我假设它主要是这样的:

 Console.WriteLine("UserInput #1");
 var response = Console.ReadLine();
 DoSomethingWith(response);
 Console.WriteLine("UserInput #2");
 response = Console.ReadLine();
 DoSomethingWith(response);
 // lather, rinse, repeat

Instead, the flow of the program will be determined by that first model. 相反,程序的流程将由该第一个模型确定。 So... 所以...

The Models 模型

The first model is the important part, but let's get the second model out of the way first. 第一个模型是重要的部分,但让我们先将第二个模型排除在外。 The second model (the AnswerModel ) is just a List<Answer> , where an Answer looks something like: 第二个模型( AnswerModel )只是一个List<Answer> ,其中Answer类似于:

public class Answer {
    public string StepName { get; set; }
    public string VerbatimResponse { get; set; }
    public object TypedResponse { get; set; }
    public Type ResponseType { get; set; }
}

It represents the answer to a particular question. 它代表对特定问题的答案。 The List of answers represents the answers to all of the user's questions so far. 答案列表表示到目前为止所有用户问题的答案。 It may be possible to play some generics games (likely with inheritance) to make the TypedResponse property actually be properly typed, this should be enough to get us started. 可能可以玩一些泛型游戏(可能具有继承性),以使TypedResponse属性实际上被正确键入,这足以使我们入门。

The first model, the InputModel is the guts of the program. 第一个模型InputModel是程序的胆量。 It will consist of a collection of ModelStep objects. 它由ModelStep对象的集合组成。 The collection could just be a simple list (question 1, question 2, etc.) or it could be a complex graph. 该集合可能只是一个简单的列表(问题1,问题2等),也可能是一个复杂的图形。 In particular, the EvalNextStep delegate property in the model shown below allows you to build a simple state machine (for example, if one of your questions is "What is your gender?", they you could have a separate path through the graph for males and for females). 特别是,通过下面显示的模型中的EvalNextStep委托属性,您可以构建一个简单的状态机(例如,如果您的问题之一是“您的性别是什么?”,那么您可以通过单独的路径访问男性)和女性)。

The InputModel would look something like this (you could adapt this for your needs): InputModel看起来像这样(您可以根据需要进行调整):

public class ModelStep {
    public string StepName { get; set; }
    public string Prompt { get; set; }
    public bool IsOptional {get; set;}
    public UserInputType InputType { get; set; }
    public TypeValidator BasicValidator { get; set; }
    public SpecificValidator AdditionalValidator { get; set; }
    public Action <List<Answer>, string> AfterInputAction { get; set; }
    public Func<List<Answer>, string, string> EvalNextStep { get; set; }
}

The StepName property is the key to everything (note that it corresponds to the StepName property of the AnswerModel). StepName属性是所有内容的键(请注意,它对应于AnswerModel的StepName属性)。 The prompt is the prompt you will use when prompting for an answer. 提示是提示答案时将使用的提示。 I'm not sure if a UserInputType property is needed, but I envision it to look something like: 我不确定是否需要UserInputType属性,但是我设想它看起来像这样:

public enum UserInputType {
    String,
    Integer,
    Numeric,
    Enum,
}

The two Validators are used to validate the user input. 这两个验证器用于验证用户输入。 The TypeValidator class would likely be abstract with concrete subclasses like: TypeValidator类可能是带有具体子类的abstract类,例如:

  • StringValidator
  • IntegerValidator
  • DoubleValidator
  • EnumValidator<T> where T : enum

A TypeValidator's role in live is to take the user's input, validate that it's the proper type, and then return either an error message or the response as a properly typed object. TypeValidator在活动中的作用是获取用户的输入,验证其是否为正确的类型,然后将错误消息或响应作为正确类型的对象返回。

SpecificValidator objects would do additional validation. SpecificValidator对象将执行其他验证。 SpecificValidator is also likely an abstract class, with concrete subclasses like: SpecificValidator也可能是abstract类,其具体子类如下:

  • LessThanValidator<T> where T : IComparable
  • GreaterThanValidator<T> where T : IComparable
  • RangneValidator<T> where T : IComparable

The AdditionalValidator property is optional. AdditionalValidator属性是可选的。 It would provide additional validation if it was needed. 如果需要,它将提供其他验证。 It would return an error message if the validation failed. 如果验证失败,它将返回错误消息。

The AfterInputAction delegate would optionally point to a function that takes all of the answers so far and the current step name, and do something with that information if needed. AfterInputAction委托可以有选择地指向一个函数,该函数采用到目前为止的所有答案和当前步骤的名称,并在需要时使用该信息进行操作。

The EvalNextStep delegate would take the same inputs as AfterInputAction delegate and return the "next step" to run. EvalNextStep委托将接受与AfterInputAction委托相同的输入,并返回“下一步”运行。 As noted above, this would allow you to create a simple "state machine". 如上所述,这将允许您创建一个简单的“状态机”。 You may not need this, but it could make it interesting. 您可能不需要它,但可能会使它变得有趣。

The Controller 控制器

The controller is the meat of the program, but it's real simple. 控制器是程序的重中之重,但这确实很简单。 At the start of your program, you'd hand the controller the InputModel and something that indicates the first step, and the controller would simply walk through the InputModel collection, prompting users and soliciting responses. 在程序开始时,您将向控制器提供InputModel和指示第一步的内容,然后控制器将简单地遍历InputModel集合,提示用户并征求响应。

However, since all user interaction is in one place, it would be easy to implement your "Quit" feature. 但是,由于所有用户交互都集中在一处,因此可以轻松实现“退出”功能。 You could also implement other similar features, like: 您还可以实现其他类似功能,例如:

  • Back (Go back to the previous question and see your answer. You could build a "back stack" and allow users to go back more than once if you wanted) 返回(返回上一个问题并查看您的答案。您可以构建“后退堆栈”,并允许用户多次返回)
  • Forward (if someone used "Back", allow them to go forward - likely with a "forward stack") 转发(如果有人使用“后退”,则允许他们前进-可能带有“转发堆栈”)
  • Skip (if the question is optional, a user could skip it) 跳过(如果问题是可选的,则用户可以跳过它)

Again, since all the interaction is in the same code, you could easily provide some sort of consistent indication as to which of these commands (Quit, Back, etc.) was allowed. 同样,由于所有交互都在同一代码中,因此您可以轻松地就允许使用哪些命令(退出,返回等)提供某种一致的指示。

The View 风景

The temptation is to have the controller directly interact with the console using Console.WriteLine, Console.ReadLine, etc. 诱惑是使控制器使用Console.WriteLine,Console.ReadLine等直接与控制台交互。

However, there is some advantage to abstracting this into a View and defining the View using an interface . 但是,将其抽象为View并使用interface定义View有一些好处。 Something like: 就像是:

public interface IConsoleView {
    void Write(string stringToWrite);
    void WriteLine(string stringToWrite);
    string ReadLine(string prompt);
}

The default implementation of this interface would be braindead easy to create using the Console class. 使用Console类可以轻松创建此接口的默认实现。 However, by making it an interface and using Dependency Injection to inject an interface implementation, you get several advantages: 但是,通过使其成为接口并使用依赖注入来注入接口实现,您将获得以下优势:

  • You make your app testable - you could inject in a test View that played prompts and recorded answers, allowing you to test your logic 您可以使应用程序可测试-您可以在测试视图中插入播放提示和记录答案的视图,从而可以测试逻辑
  • You could have a ResponseFileView that allow you to accept a "response file" that consists of the answers to the questions, allowing automation of your UI 您可能有一个ResponseFileView ,它允许您接受包含问题答案的“响应文件”,从而实现UI的自动化
  • etc. 等等

You might want to extend the definition of the interface from what I have above (possibly having do-nothing implementations of the extra functions in your default Console class implementation). 您可能想要从上面的内容扩展接口的定义(可能在默认Console类实现中具有多余功能的不做任何实现)。 For example: 例如:

void WriteStepName(string stepName);
void WriteUserResponse (string userResponse);

Functions like these might be useful in the test and response file scenarios. 这些功能在测试和响应文件方案中可能很有用。 You would provide empty implementations in the normal view. 您将在普通视图中提供空的实现。

Sorry this went on a for a while, but I've been thinking about it the last day or so. 抱歉,这种情况持续了一段时间,但我在最后一天左右一直在考虑。 Whatever you do, don't try to do this with additional threads, that will just cause you headaches. 无论您做什么,都不要尝试使用其他线程来执行此操作,这只会使您头痛。

Simple Answer: Just use ctrl + C to exit the Console anytime (no code needed) 简单答案:随时使用ctrl + C退出控制台(无需代码)

If you want to do some clean up operations before existing: then you are probably looking for Console.CancelKeyPress Event 如果要在存在之前进行一些清理操作:则可能正在寻找Console.CancelKeyPress事件

private static volatile bool cancelRequested = false;

public static void Main(string[] args)
{
    Console.CancelKeyPress += new ConsoleCancelEventHandler(ExitConsole);
    while (!cancelRequested)
    {
        // here your program will continue a WHOLE WHILE loop until user requests exit by 
        // pressing either the C console key (C) or the Break key 
        // (Ctrl+C or Ctrl+Break).

        UserInput #1;
        UserInput #2;
        UserInput #3;
    }
}

static void ExitConsole(object sender, ConsoleCancelEventArgs e)
{
    e.Cancel = true;
    cancelRequested = true;
}

Here you can find relative answers. 在这里您可以找到相对的答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何允许用户随时通过输入退出控制台 C# - How to allow a user to exit from the Console at any time C# with an input 等待用户输入C#console app的设置时间 - Wait set time for user input C# console app C#我如何要求用户在输入无效输入时按任意键退出 - c# how do i ask the user to press any keys to exit when they enter an invalid input C#:检查控制台中是否按下了任何键 - C#: Check if any key was pressed in Console 计算跨不同浏览器的页面加载时间(如果没有任何技术,最好使用C#控制台应用程序) - calculating the page load time on fly across different browser(C# console application preferbly if not any technology) C#控制台应用程序-显式终止(非Env..Exit) - C# console app - explicit termination (not Env..Exit) 带有任务计划程序的C#控制台应用程序退出代码 - C# Console App Exit Code with Task Scheduler C#控制台应用程序Environment.Exit(0)无法识别为关闭事件 - C# Console app Environment.Exit(0) not recognised as a shutdown event 有什么办法可以让我在 c# 控制台应用程序中同时播放两个声音? - Is there any way i can somehow have two sounds play at once in c# console app? c#-控制台应用程序-解决用户按任意数字输入时的错误 - c# - Console App - Solving error when user presses enter without any numbers
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM