当输入被重定向时,我可以绕过 Console.ReadKey() 问题吗?

Can I bypass Console.ReadKey() issue when input is redirected?

我是一名 C# 老师,我为我的学生编写了一些自动化硬件检查器。 学生编写 C# 控制台应用程序。我的硬件检查器基于输入重定向,因此我可以在我自己生成的输入上测试他们的代码。

问题是学生有时会用Console.ReadKey()指令结束他们的程序(他们这样做只是为了让执行window在他们运行F5下的程序时不关闭-调试)。 Console.ReadKey() 在输入重定向下 运行 时崩溃,但出现以下异常:

System.InvalidOperationException: Cannot read keys when either application does not have a console or when console input has been redirected from a file.

我有什么办法可以解决"bypass"这个问题(不改变学生代码)?也许告诉 Console 忽略 ReadKey 指令?

我看到了依赖注入模式的明确案例。

让我们构建一个简单的示例,使用多态的 ReadReadLineWriteLine 功能:您的学生必须写一份家庭作业,其中 Console.ReadLine() 中给出的数字必须解析为 int 并返回到 Console Window.

通常学生会这样写:

class Program
{
    static void Main(string[] args)
    {
        var stringValue = Console.ReadLine();
        int number;

        if (int.TryParse(stringValue, out number))
            Console.WriteLine($"The double of {number} is {number * 2}");
        else
            Console.WriteLine($"Wrong input! '{stringValue}' is not an integer!");

        Console.Read();
    }
}

现在,改为为 Console 功能创建一个 interface

public interface IOutput
{
    void Read();
    string ReadLine();
    void WriteLine(string text);
}

学生必须创建一个 Homework class 来包装所有必需的作业代码,以这种方式使用 IOutput 实例:

public class HomeWork
{
    private IOutput _output;

    public HomeWork(IOutput output)
    {
        _output = output;
    }

    public void Run()
    {
        _output.WriteLine("Give me an integer:");

        var stringValue = _output.ReadLine();

        int number;

        if (int.TryParse(stringValue, out number))
            _output.WriteLine($"The double of {number} is {number * 2}");
        else
            _output.WriteLine($"Wrong input! '{stringValue}' is not an integer!");

        _output.Read();
    }
}

Main变成:

static void Main(string[] args)
{
    var h = new HomeWork(new ConsoleOutput());
    h.Run();
}

你也给他们 ConsoleOutput class:

public class ConsoleOutput : IOutput
{
    public void Read()
    {
        Console.Read();
    }

    public string ReadLine()
    {
        return Console.ReadLine();
    }

    public void WriteLine(string text)
    {
        Console.WriteLine(text);
    }
}

所以使用它而不是直接调用Console.Read()等等

学生必须传给你的不是整个 Application,而是 Homework class

您可以创建一个测试 class,它使用 Homework classIOutput 的一些测试实现,如下所示:

public abstract class TestOutput : IOutput
{
    public TestOutput()
    {
        Outputs = new List<string>();
    }

    public void Read()
    {
        //do nothing?
    }

    public abstract string ReadLine();

    public void WriteLine(string text)
    {
        Outputs.Add(text);
    }

    public List<string> Outputs { get; set; }
}

public class TestOutputWithAValidNumber : TestOutput
{
    public TestOutputWithAValidNumber(int value)
    {
        Value = value;
    }

    public override string ReadLine()
    {
        return Value.ToString();
    }

    public int Value { get; }
}

public class TestOutputWithNotValidNumber : TestOutput
{
    public TestOutputWithNotValidNumber(string value)
    {
        Value = value;
    }

    public override string ReadLine()
    {
        return Value;
    }

    public string Value { get; }
}

测试class可以是这样的:

[TestClass]
public class TestOutputClass
{
    [TestMethod]
    public void TestGoodNumber()
    {
        var testOutput = new TestOutputWithAValidNumber(1234);

        var h = new HomeWork(testOutput);

        h.Run();

        Assert.AreEqual(1234, testOutput.Value);
        Assert.AreEqual("Give me an integer:", testOutput.Outputs[0]);
        Assert.AreEqual("The double of 1234 is 2468", testOutput.Outputs[1]);
    }

    [TestMethod]
    public void TestWrongNumber()
    {
        var testOutput = new TestOutputWithNotValidNumber("foo");

        var h = new HomeWork(testOutput);

        h.Run();

        Assert.AreEqual("foo", testOutput.Value);
        Assert.AreEqual("Give me an integer:", testOutput.Outputs[0]);
        Assert.AreEqual("Wrong input! 'foo' is not an integer!", testOutput.Outputs[1]);
    }
}

如果您只需要包装 Console.Read() 方法,请随意简化所有这些代码,但恕我直言,我认为对这个可能的解决方案有更广泛的看法无论如何都会有用。

如果可执行文件在 IL 中,您可以创建一个使用 ILDASM.

的简单应用程序

关键点是:使用 ILDASM 将可执行文件反汇编为文本 file/stream,查找对 Console.Read 的任何调用并将其删除,然后重新编译它并 运行.