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