Могу ли я обойти проблему Console.ReadKey(), когда ввод перенаправлен?

Я учитель C#, и я написал несколько автоматических проверок HW для моих учеников. Студенты пишут консольные приложения на C#. Моя проверка HW основана на перенаправлении ввода, поэтому я могу проверить их код на своем собственном сгенерированном вводе.

Проблема в том, что студенты иногда заканчивают свою программу Console.ReadKey() инструкция (они делают это только для того, чтобы окно выполнения не закрывалось, когда они запускали программу под F5 - Debug). Console.ReadKey() аварийно завершает работу при перенаправлении ввода со следующим исключением:

System.InvalidOperationException: Невозможно прочитать ключи, если у любого приложения нет консоли или когда ввод консоли был перенаправлен из файла.

Есть ли у меня какой-либо способ "обойти" эту проблему (без изменения кода студентов)? Может сказать Console игнорировать ReadKey инструкции?

2 ответа

Решение

Если исполняемые файлы находятся в IL, вы можете создать простое приложение, которое использует ILDASM.

Ключевой момент: разберите исполняемый файл с ILDASM в текстовый файл / поток, ищите любой вызов Console.Read и удалите его, затем перекомпилируйте и запустите.

Я вижу ясный пример паттерна внедрения зависимостей.

Давайте построим простой пример, с Read, ReadLine а также WriteLine полиморфно: ваши студенты должны написать домашнее задание, в котором число, указанное в Console.ReadLine() должен быть разобран как int и вернулся к Console Окно.

Обычно студент пишет что-то вроде:

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

Теперь вместо этого создайте interface для Console функциональные возможности:

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

Студент должен создать Homeworkclass который оборачивает весь необходимый код домашней работы, используя 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();
}

Вы также даете им ConsoleOutputclass:

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, но только Homeworkclass,

Вы можете создать тестовый класс, который использует Homeworkclass с некоторыми тестовыми реализациями IOutput как следующие:

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

Тестовый класс может быть примерно таким:

[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() Метод, не стесняйтесь упростить весь этот код, но ИМХО Я думал, что более широкий взгляд на это возможное решение был бы полезен в любом случае.

Другие вопросы по тегам