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



我有一个相对较大的控制台应用程序,具有多个菜单和用户输入。我需要为用户创建一种在任何时候"退出"或"返回"的方式,本质上是break;当前正在进行的方法。我考虑过使用一大堆条件句,但这需要一些时间,而且不太干净。是否有任何方法可以持续检查"Q"是否退出并在整个项目中运行基于该输入的方法?

伪代码


我现在拥有的:

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

我对尝试的想法:

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;

我想要什么:

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

我可以通过将它硬编码到每一个方法中来解决它,但必须有更好的方法。此外,我需要向控制台输出,每个输入都可以选择"Q"退出。

自90年代初以来,我还没有做过复杂的控制台应用程序。如果我有一个"具有多个菜单和用户输入"的应用程序,我通常会使用本机支持它的东西(Windows窗体、WPF、web应用程序)。但是

如果这是一个足够大/复杂的项目,特别是如果您计划编写多个这样的项目,那么编写一个基于模型-视图-控制器(MVC)模式的小框架可能是值得的。

在这种情况下,我们实际上有两个模型,一个是描述程序流的相当复杂的模型,另一个是包含用户答案的简单Dictionary。控制器是一个简单的处理循环,执行第一个模型中的指令。视图非常简单,但我们将看到,通过将其分离,有一些优点。

要做到这一点,您需要完全改变编程的结构。我想它主要看起来是这样的:

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

相反,程序的流程将由第一个模型决定。所以…

模型

第一个模型是重要的部分,但让我们先把第二个模型排除在外。第二个模型(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; }
}

它代表了一个特定问题的答案。答案列表表示到目前为止用户所有问题的答案。也许可以玩一些泛型游戏(可能有继承)来使TypedResponse属性真正正确地类型化,这应该足以让我们开始。

第一个模型,InputModel是程序的核心。它将由ModelStep对象的集合组成。集合可以只是一个简单的列表(问题1、问题2等),也可以是一个复杂的图。特别是,下面显示的模型中的EvalNextStep委托属性允许您构建一个简单的状态机(例如,如果您的问题之一是"您的性别是什么?",那么对于男性和女性,您可以在图中有一个单独的路径)。

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; }
}

StepName属性是一切的关键(请注意,它对应于AnswerModel的StepName属性)。提示是在提示回答时使用的提示。我不确定是否需要UserInputType属性,但我设想它看起来像:

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

这两个验证器用于验证用户输入。TypeValidator类很可能是abstract,具有具体的子类,如:

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

TypeValidator在live中的作用是接受用户的输入,验证其类型是否正确,然后返回错误消息或作为正确类型对象的响应。

SpecificValidator对象将进行额外的验证。SpecificValidator也可能是abstract类,具有具体的子类,如:

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

AdditionalValidator属性是可选的。如果需要,它将提供额外的验证。如果验证失败,它将返回一条错误消息。

AfterInputAction委托可以选择指向一个函数,该函数获取到目前为止的所有答案和当前步骤名称,并在需要时使用这些信息。

EvalNextStep委托将接受与AfterInputAction委托相同的输入,并返回要运行的"下一步"。如上所述,这将允许您创建一个简单的"状态机"。你可能不需要这个,但它可以让它变得有趣。

控制器

控制器是程序的核心,但它非常简单。在程序开始时,您会将InputModel和指示第一步的东西交给控制器,控制器只需遍历InputModel集合,提示用户并请求响应。

然而,由于所有用户交互都在一个地方,所以实现"退出"功能会很容易。您还可以实现其他类似的功能,如:

  • 返回(返回上一个问题并查看您的答案。如果您愿意,您可以构建一个"后堆栈"并允许用户多次返回)
  • 前进(如果有人使用"后退",则允许他们前进-可能使用"前进堆栈")
  • 跳过(如果问题是可选的,用户可以跳过它)

同样,由于所有交互都在同一个代码中,因此您可以很容易地提供某种一致的指示,指示哪些命令(Quit、Back等)是允许的。

视图

诱惑是让控制器使用console直接与控制台交互。WriteLine,控制台。ReadLine等

但是,将其抽象为View并使用interface定义视图也有一些好处。类似于:

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

该接口的默认实现将非常容易使用Console类创建。然而,通过将其作为接口并使用依赖注入注入接口实现,您可以获得以下几个优势:

  • 你让你的应用程序可测试-你可以在一个测试视图中插入,该视图可以播放提示和记录答案,让你可以测试你的逻辑
  • 您可以有一个ResponseFileView,它允许您接受一个由问题答案组成的"响应文件",从而实现UI的自动化
  • 等等

您可能希望从上面的内容扩展接口的定义(可能在默认的Console类实现中对额外的函数没有任何实现)。例如:

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

像这样的函数在测试和响应文件场景中可能很有用。您将在正常视图中提供空的实现。

很抱歉,这种情况持续了一段时间,但我在最后一天左右一直在考虑。无论你做什么,都不要试图用额外的线程来做,这只会让你头疼。

简单答案:只需使用ctrl + C随时退出控制台(无需代码)

如果您想在存在之前进行一些清理操作:,那么您可能正在寻找Console。取消按键事件

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;
}

在这里你可以找到相对的答案。

最新更新