【问题标题】:Exit Console App at any Time During Any Input (C#)在任何输入期间随时退出控制台应用程序 (C#)
【发布时间】:2019-02-25 03:33:24
【问题描述】:

我有一个相对较大的控制台应用程序,其中包含多个菜单和用户输入。我需要为用户创建一种随时“退出”或“返回”的方式,基本上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”选项可以退出每个输入。

【问题讨论】:

  • 可以编写一个名为InputProcessorprocessCommand()的自定义类,其属性quitCommand可以设置为“Q”。
  • 你可能正在寻找这个stackoverflow.com/questions/177856/…
  • @TiefanJu 你会不会碰巧有一个类似的链接。我还在学习 C#,所以我绝对不熟悉这个。
  • stackoverflow.com/questions/8898182/… 中的内容会帮助您思考。
  • 这里的“退出”是什么意思?您想退出整个应用程序,还是返回主菜单?

标签: c#


【解决方案1】:

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

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

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

为此,您需要彻底改变程序的结构。我假设它大多看起来像这样:

 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 等),也可能是一个复杂的图表。特别是,下面显示的模型中的EvalNextStepdelegate 属性允许您构建一个简单的状态机(例如,如果您的问题之一是“您的性别是什么?”,那么您可以有一条单独的路径通过图表男性和女性)。

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&lt;T&gt; where T : enum

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

SpecificValidator 对象会做额外的验证。 SpecificValidator 也可能是 abstract 类,具有具体的子类,例如:

  • LessThanValidator&lt;T&gt; where T : IComparable
  • GreaterThanValidator&lt;T&gt; where T : IComparable
  • RangneValidator&lt;T&gt; where T : IComparable

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

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

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

控制器

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

但是,由于所有用户交互都在一个地方,因此很容易实现您的“退出”功能。您还可以实现其他类似的功能,例如:

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

同样,由于所有交互都在同一个代码中,您可以轻松地提供某种一致的指示,说明允许使用哪些命令(退出、返回等)。

视图

诱惑是让控制器使用 Console.WriteLine、Console.ReadLine 等直接与控制台交互。

但是,将其抽象为 View 并使用 interface 定义 View 有一些优势。比如:

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

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

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

【讨论】:

    【解决方案2】:

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

    如果你想在存在之前做一些清理操作:那么你可能正在寻找Console.CancelKeyPress Event

    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你可以找到相关答案。

    【讨论】:

    • 这应该是评论,而不是答案。
    • 为什么?,规则是什么?
    • @BakriBitar 你并没有真正回答这个问题。如果您从该链接中包含一个示例,那么只需将他们介绍到哪里会更好。
    • 你的回答基本上是一个链接转储。您应该投票(或标记)作为副本关闭,将链接发布为评论,或者写一个正确的答案,这样如果链接中断,您的答案仍然有效。引用链接的相关部分,考虑添加一个小代码示例,这将使它成为一个正确的答案。
    • Console.CancelKeyPress 不会要求我在每次输入后添加一个条件,或者这会检测到恒定流吗?
    猜你喜欢
    • 1970-01-01
    • 2023-03-21
    • 2014-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多