So I wrote this for my Mandelbrot Generator, to collect and use input from the user, and I made it dynamic enough to be useful anywhere.
I'm really interested in all critiques of it, as I'm going to be using it a lot now.
The idea is to allow the user to easily show messages on the console, that require input. Once valid input is received, the method returns.
Example usage:
int numberOfCores = Environment.ProcessorCount - 1;
numberOfCores = Prompt($"Enter the number of cores to use (1 to {Environment.ProcessorCount})",
false,
numberOfCores,
$"The value must be between 1 and {Environment.ProcessorCount}",
delegate (int x) { return x >= 1 && x <= Environment.ProcessorCount; });
This will prompt the user to enter a value between 1
and Environment.ProcessorCount
. But, you could also use:
ushort maxIterations = 1000;
maxIterations = Prompt("Enter the maximum number of iterations", false, maxIterations);
Which will only prompt the user for a valid ushort
value.
/// <summary>
/// This will repeatedly prompt the user with a message and request input, then return said input (if valid).
/// </summary>
/// <typeparam name="T">The type of input that should be returned.</typeparam>
/// <param name="message">The message to initally display to the user.</param>
/// <param name="requireValue">Whether or not to allow use of a `defaultValue`.</param>
/// <param name="defaultValue">The default value to be returned if a user enters an empty line (`""`).</param>
/// <param name="failureMessage">The message to display on a failure. If null, then the `message` parameter will be displayed on failure.</param>
/// <param name="validationMethod">An optional delegate to a method which can perform additional validation if the input is of the target type.</param>
/// <returns>The input collected from the user when it is deemed valid.</returns>
static T Prompt<T>(string message, bool requireValue, T defaultValue = default(T), string failureMessage = null, Func<T, bool> validationMethod = null)
where T : struct
{
if (!requireValue)
Console.Write(string.Format(message + " [{0}]: ", defaultValue));
else
Console.Write(message + ": ");
bool pass = false;
T result = default(T);
while (!pass)
{
string line = Console.ReadLine();
if (requireValue)
{
pass = Retrieve(line, out result);
if (pass && validationMethod != null)
pass = validationMethod(result);
}
else
{
if (line != "")
{
pass = Retrieve(line, out result);
if (pass && validationMethod != null)
pass = validationMethod(result);
}
else
{
pass = true;
result = defaultValue;
}
}
if (!pass)
{
Console.WriteLine("Invalid value [{0}]", line);
if (failureMessage != null)
{
if (!requireValue)
Console.Write(string.Format(failureMessage + " [{0}]: ", defaultValue));
else
Console.Write(failureMessage + ": ");
}
else
{
if (!requireValue)
Console.Write(string.Format(message + " [{0}]: ", defaultValue));
else
Console.Write(message + ": ");
}
}
}
return result;
}
private static bool Retrieve<T>(string line, out T resultValue)
where T : struct
{
var type = typeof(T);
resultValue = default(T);
bool pass = false;
if (type == typeof(short))
{
short result = 0;
pass = short.TryParse(line, out result);
resultValue = (T)(object)result;
}
else if (type == typeof(int))
{
int result = 0;
pass = int.TryParse(line, out result);
resultValue = (T)(object)result;
}
else if (type == typeof(float))
{
float result = 0f;
pass = float.TryParse(line, out result);
resultValue = (T)(object)result;
}
else if (type == typeof(double))
{
double result = 0f;
pass = double.TryParse(line, out result);
resultValue = (T)(object)result;
}
else if (type == typeof(sbyte))
{
sbyte result = 0;
pass = sbyte.TryParse(line, out result);
resultValue = (T)(object)result;
}
else if (type == typeof(byte))
{
byte result = 0;
pass = byte.TryParse(line, out result);
resultValue = (T)(object)result;
}
else if (type == typeof(ushort))
{
ushort result = 0;
pass = ushort.TryParse(line, out result);
resultValue = (T)(object)result;
}
else if (type == typeof(uint))
{
uint result = 0;
pass = uint.TryParse(line, out result);
resultValue = (T)(object)result;
}
else if (type == typeof(long))
{
long result = 0;
pass = long.TryParse(line, out result);
resultValue = (T)(object)result;
}
else if (type == typeof(ulong))
{
ulong result = 0;
pass = ulong.TryParse(line, out result);
resultValue = (T)(object)result;
}
return pass;
}
Additional notes: if anyone would like to use this in your projects, you are more than welcome to.