太多的方法非常相似



我有很多方法非常相似,如下代码所示:

public static void ReadFromKeyboard(string label, out int retVal)
{
try
{
Console.Write(label);
retVal = int.Parse(Console.ReadLine());
}
catch (Exception)
{
Console.WriteLine("Please insert int value.");
ReadFromKeyboard(label, out retVal);
}
}
public static void ReadFromKeyboard(string label, out float retVal)
{
try
{
Console.Write(label);
retVal = float.Parse(Console.ReadLine());
}
catch (Exception)
{
Console.WriteLine("Please insert float value.");
ReadFromKeyboard(label, out retVal);
}
}
public static void ReadFromKeyboard(string label, out double retVal)
{
try
{
Console.Write(label);
retVal = double.Parse(Console.ReadLine());
}
catch (Exception)
{
Console.WriteLine("Please insert double value.");
ReadFromKeyboard(label, out retVal);
}
}

另一方面,我不知道该调用哪个方法。我只会在运行时发现它。

我有没有办法把这些方法重写成一个名为"ReadFromKeyboard"的方法,根据作为参数传递给它的类型,它可以返回int、float或double?

谢谢!

正如其他答案所示,您可以通过各种技术消除重复的代码,所有这些都很可怕,您不应该这样做。

特别是,不要试图使用泛型来解决这个"问题">泛型适用于代码为泛型的情况。这就是为什么它们被称为泛型!也就是说,代码在所有可能的类型上都是相同的。您的示例与泛型代码相反;对于少数类型,您有不同的规则,处理这种情况的方法是完全按照您已经做的操作:每个不同的规则实现一个方法

我在引号中说"问题"是因为你在这里实际上没有问题要解决,所以不要试图解决它。写六个类似的简短方法对作者或维护人员来说并不是一个主要负担。

现在,也就是说,你的代码也没有它可能的那么好,你应该重写它。正确的代码编写方法是:

public static int ReadInteger(string label)
{
while(true)
{
int value;
Console.Write(label);
string read = Console.ReadLine();
bool success = int.TryParse(read, out value);
if (success)
return value;
Console.WriteLine("Please type an integer value.");
}
}

您最初实现的问题是:

  • 不要将异常处理用作主线控制流。如果可以避免异常,请不要捕获异常。这就是TryParse的作用
  • 不要将递归用作无边界循环。如果你想要一个无界循环,那就是while(true)的作用。记住,默认情况下,C#不是尾部递归的
  • 无需使用参数。方法在逻辑上返回一个整数,所以实际上返回了一个整数。重命名它,这样就不会与其他读取方法发生冲突。让调用者在ReadInteger上写Read<int>没有什么令人信服的好处,而避免out参数有很多令人信服的优点

我已经尝试根据Eric Lippert的配方来实现代码。下方的代码

  1. 不使用异常处理作为主线控制流
  2. 根本不使用递归
  3. 无需使用输出参数

private static void Main(string[] args)
{
int intValue = ReadFromKeyboardInt32("enter int");
float floatValue = ReadFromKeyboardSingle("enter float");
double doubleValue = ReadFromKeyboardDouble("enter double");
Console.WriteLine($"{intValue}, {floatValue}, {doubleValue}");
}
public static Double ReadFromKeyboardDouble(string label) =>
ReadFromKeyboard(label, (text) => (Double.TryParse(text, out var value), value));
public static Int32 ReadFromKeyboardInt32(string label) =>
ReadFromKeyboard(label, (text) => (Int32.TryParse(text, out var value), value));
public static Single ReadFromKeyboardSingle(string label) =>
ReadFromKeyboard(label, (text) => (Single.TryParse(text, out var value), value));
public static T ReadFromKeyboard<T>(string label, Func<string, (bool, T)> tryParse)
{
for (; ; )
{
Console.Write($"{label}: ");
var result = tryParse(Console.ReadLine());
if (result.Item1)
{
return result.Item2;
}
Console.WriteLine($"Please enter valid {typeof(T).Name} value");
}
}

不列出所有可能的类型(您可能事先不知道(,而是可以使用System.Convert类,特别是Convert.ChangeType()方法。作为概念验证,你可以使用这样的方法:

public static void ReadFromKeyboard<T>(string label, out T result) {
Type targetType = typeof(T);
Console.Write($"{label}: ");
string input = Console.ReadLine();
object convertedValue = Convert.ChangeType(input, targetType);
result = (T)convertedValue;
}

你可以这样使用这个方法:

public static void Main(string[] args) {
ReadFromKeyboard("enter a double", out double d);
ReadFromKeyboard("enter an int", out int i);
Console.WriteLine($"double: {d}");
Console.WriteLine($"int: {i}");
}

这样就可以使用您想要的任何类型(假设Convert类支持它(。显然,如果您愿意,可以在ReadFromKeyboard方法中添加异常处理和do-while循环。

如果您想依靠运行时的重载解析来决定调用哪个方法,那么您必须为将支持的每种类型都有一个单独的方法。这就是它的工作原理。

另一方面,如果您可以允许用户提供至少一点类型信息,那么我们可以通过删除try/catch并使用真正的return语句来改进泛型。你可以这样称呼它:

var myNumber = ReadFromKeyboard<double>("Enter a double: ");

代码看起来是这样的:

public static T ReadFromKeyboard<T>(string label, int maxRetries = int.MaxValue)
{
while (maxRetries >= 0)
{
Console.Write(label);
if (typeof(T) == typeof(int))
{
int result;
if (int.TryParse(Console.ReadLine(), out result)) return (T)(object)result;
}
if (typeof(T) == typeof(float))
{
float result;
if (float.TryParse(Console.ReadLine(), out result)) return (T)(object)result;
}
else if (typeof(T) == typeof(double))
{
double result;
if (double.TryParse(Console.ReadLine(), out result)) return (T)(object)result;
}
else if (typeof(T) == typeof(decimal))
{
decimal result;
if (decimal.TryParse(Console.ReadLine(), out result)) return (T)(object)result;
}
else 
throw new InvalidOperationException("Unsupported type");
maxRetries--;
}
throw new InvalidOperationException("Too many bad inputs");
}

但你必须做一些非常粗糙的选角和类型检查才能使其工作。这仍然有可能引发异常,这似乎是你想要避免的,但如果你的用户坐在那里尝试超过20亿次,我怀疑他们会非常惊讶。

相关内容

最新更新