从抛出的异常中恢复或 return
Recover or return from a thrown exception
多年来我的主要语言是 Perl,我定期验证用户输入没有问题。现在我正在使用大量 C#,并希望迁移到 throw/catch 验证用户输入的样式和 recovering/returning 抛出的异常。我正在使用一种非常天真的(即愚蠢的)方法来做这件事,并且感到迫切需要转向更成熟和不那么愚蠢的东西。我从提示中复制了一个 returns 整数的函数。我正在使用可怕的 GOTO 语句从用户错误中恢复。执行此操作的更好方法是什么?
谢谢,抄送。
private static int GetInput(string v)
{
begin:
Console.Write(v);
string strradius = Console.ReadLine();
int intradius;
try
{
intradius = int.Parse(strradius);
if (intradius < 1)
throw new ArgumentOutOfRangeException();
}
catch (ArgumentNullException)
{
Console.WriteLine("You must enter a value.");
goto begin;
}
catch (FormatException)
{
Console.WriteLine("You must enter a valid number.");
goto begin;
}
catch (ArgumentOutOfRangeException)
{
Console.WriteLine("Your number is out of range");
goto begin;
}
catch (Exception ex)
{
Console.WriteLine(ex);
goto begin;
}
finally
{
Console.WriteLine("Okay");
}
return intradius;
}
首先,关于何时使用 goto
的一个好的经验法则是从不。真的,除了少数非常罕见的特殊情况,你永远不想使用它。
接下来,对于您的问题,使用异常来验证输入通常不是一个好主意。正如大多数人指出的那样,它很昂贵。异常应该用于处理异常情况,所以我实际上根本不会使用它们。
相反,您可以使用 do-while
循环,并在用户输入错误时重复。一旦获得正确的输入,就退出循环。如果万一发生异常,你不应该真的继续这个过程。要么在外部处理它(即,在您的方法中没有 try-catch
),要么如果您必须执行 try-catch
则只需打印一条消息并退出该方法。但是我不会对这样的方法使用异常处理。将 return 类型实际更改为 bool 也是一个好主意,这样您就可以通过 return 类型向外界表明该方法是否成功。您使用 out
参数实际 return 转换后的 int
.
private static bool GetInput(string msg, out int converted)
{
bool result = false;
converted = 0;
do
{
Console.Write(msg);
string str = Console.ReadLine();
result = int.TryParse(str, out converted);
if (result && converted < 1)
{
Console.WriteLine("Your number is out of range");
result = false;
}
if (!result && string.IsNullOrEmpty(str))
{
Console.WriteLine("You must enter a value.");
}
if (!result && !string.IsNullOrEmpty(str))
{
Console.WriteLine("You must enter a valid number.");
}
} while (!result);
return result;
}
我会这样写(虽然我可能会给用户一个放弃的机会):
private static int GetInput(string v)
{
int intradius = 0; //needs to be initialized to keep compiler happy
while (true)
{
Console.Write($"{v}: ");
string strradius = Console.ReadLine();
if (!int.TryParse(strradius, out intradius))
{
Console.WriteLine($"An integer is required: [{strradius}] is not an integer");
}
else if (intradius < 1)
{
Console.WriteLine($"The entered number [{intradius}] is out of range, it must be one or greater");
}
else
{
break; //breaking out of the while loop, the input is good
}
}
return intradius;
}
在 C# 代码中使用 goto 语句是非常不受欢迎的,因为它会使代码难以阅读、调试和维护(有关详细信息,请阅读 this)。可以使用循环、if/then 语句或方法调用来代替 goto 语句。此外,应谨慎使用 try \ catch
块,以捕获您无法处理的异常。
在您的情况下,我们可以使用 while
循环继续循环直到输入有效数字,然后我们可以使用 int.TryParse
method 尝试解析字符串并获取整数结果。此方法 return 是一个 Boolean
表示成功,并采用 out
参数,该参数将设置为整数结果。
我对你的方法的建议是让它接受一个字符串,该字符串将用作用户的提示(要求他们输入一个数字),return 他们输入的整数结果.
例如:
private static int GetIntFromUser(string prompt, int minValue = int.MinValue,
int maxValue = int.MaxValue)
{
int result;
string errorMsg = $"ERROR: Input must be a valid number from {minValue} to {maxValue}";
while(true)
{
Console.Write(prompt);
string input = Console.ReadLine();
if (!int.TryParse(input, out result) || result < minValue || result > maxValue)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(errorMsg);
Console.ResetColor();
}
else
{
break;
}
}
return result;
}
实际上,我们现在可以调用此方法从用户那里获取号码,我们将知道它们是有效的,而无需进行任何额外的验证:
private static void Main()
{
// Age must be between 5 and 100
var age = GetIntFromUser("Please enter your age: ", 5, 100);
// Weight must be a positive number (minimum of zero)
var weight = GetIntFromUser("Please enter your weight: ", 0);
// No restrictions on favorite number
var favNum = GetIntFromUser("Enter your favorite whole number: ");
// This is a similar method I wrote to pause the program with a prompt
GetKeyFromUser("\nDone! Press any key to exit...");
}
输出
对于可恢复验证,使用条件 code/checks 而不是依赖异常。从性能角度来看,异常是昂贵的,主要是因为它们会生成调用堆栈。
相反,看看这样的东西:
private static int GetInput(string v)
{
Console.Write(v);
string strradius = Console.ReadLine();
if (string.IsNullOrEmpty(strradius)
{
Console.WriteLine("You must enter a value.");
return 0;
}
int intradius;
bool result = int.TryParse(strradius, out intradius);
if (!result)
Console.WriteLine("You must enter a valid number.");
else if (intradius < 1)
Console.WriteLine("Your number is out of range");
Console.WriteLine("Okay");
return intradius;
}
就个人而言,我喜欢包装业务逻辑结果:
// Result container.
private class RadiusValidationResults
{
public bool IsSuccessful {get; private set;}
public int Radius {get; private set;}
public string FailureReason {get; private set;}
private RadiusValidationResults()
{ }
public static RadiusValidationResults Success(int result)
{
return new RadiusValidationResults { IsSuccessful = true, Radius = result };
}
public static RadiusValidationResults Failure(string failureReason)
{
return new RadiusValidationResults { FailureReason = failureReason };
}
}
// Validation business logic.
private static RadiusValidationResult ValidateRadius(string input)
{
if (string.IsNullOrEmpty(input)
return RadiusValidationResult.Failure("You must enter a value.");
int radius;
if (!int.TryParse(strradius, out radius))
return RadiusValidationResult.Failure("You must enter a valid number.");
else if (intradius < 1)
return RadiusValidationResult.Failure("Your number is out of range");
return RadiusValidationResult.Success(radius);
}
那么你与控制台交互的调用方法:
private static int GetInput()
{
try
{
var result = ValidateRadius(Console.ReadLine());
if(!result.IsSuccessful)
Console.WriteLine(result.FailureReason);
else
Console.WriteLine("Okay");
return result.Radius;
catch // Here you can handle specific exception types, or bubble to a higher level. Log exception details and either terminate or present users with a generic "Whoops" and let them retry the operation.
{
Console.WriteLine("An unexpected error occurred.")
}
}
这意味着您的业务逻辑(验证)可以在不硬依赖数据源或输出的情况下进行单元测试。 (控制台)代码应该简洁易懂。可以将 Try/Catch 添加到 GetInput 以处理异常情况。通常让异常冒泡到足够高的级别来处理它们。
多年来我的主要语言是 Perl,我定期验证用户输入没有问题。现在我正在使用大量 C#,并希望迁移到 throw/catch 验证用户输入的样式和 recovering/returning 抛出的异常。我正在使用一种非常天真的(即愚蠢的)方法来做这件事,并且感到迫切需要转向更成熟和不那么愚蠢的东西。我从提示中复制了一个 returns 整数的函数。我正在使用可怕的 GOTO 语句从用户错误中恢复。执行此操作的更好方法是什么?
谢谢,抄送。
private static int GetInput(string v)
{
begin:
Console.Write(v);
string strradius = Console.ReadLine();
int intradius;
try
{
intradius = int.Parse(strradius);
if (intradius < 1)
throw new ArgumentOutOfRangeException();
}
catch (ArgumentNullException)
{
Console.WriteLine("You must enter a value.");
goto begin;
}
catch (FormatException)
{
Console.WriteLine("You must enter a valid number.");
goto begin;
}
catch (ArgumentOutOfRangeException)
{
Console.WriteLine("Your number is out of range");
goto begin;
}
catch (Exception ex)
{
Console.WriteLine(ex);
goto begin;
}
finally
{
Console.WriteLine("Okay");
}
return intradius;
}
首先,关于何时使用 goto
的一个好的经验法则是从不。真的,除了少数非常罕见的特殊情况,你永远不想使用它。
接下来,对于您的问题,使用异常来验证输入通常不是一个好主意。正如大多数人指出的那样,它很昂贵。异常应该用于处理异常情况,所以我实际上根本不会使用它们。
相反,您可以使用 do-while
循环,并在用户输入错误时重复。一旦获得正确的输入,就退出循环。如果万一发生异常,你不应该真的继续这个过程。要么在外部处理它(即,在您的方法中没有 try-catch
),要么如果您必须执行 try-catch
则只需打印一条消息并退出该方法。但是我不会对这样的方法使用异常处理。将 return 类型实际更改为 bool 也是一个好主意,这样您就可以通过 return 类型向外界表明该方法是否成功。您使用 out
参数实际 return 转换后的 int
.
private static bool GetInput(string msg, out int converted)
{
bool result = false;
converted = 0;
do
{
Console.Write(msg);
string str = Console.ReadLine();
result = int.TryParse(str, out converted);
if (result && converted < 1)
{
Console.WriteLine("Your number is out of range");
result = false;
}
if (!result && string.IsNullOrEmpty(str))
{
Console.WriteLine("You must enter a value.");
}
if (!result && !string.IsNullOrEmpty(str))
{
Console.WriteLine("You must enter a valid number.");
}
} while (!result);
return result;
}
我会这样写(虽然我可能会给用户一个放弃的机会):
private static int GetInput(string v)
{
int intradius = 0; //needs to be initialized to keep compiler happy
while (true)
{
Console.Write($"{v}: ");
string strradius = Console.ReadLine();
if (!int.TryParse(strradius, out intradius))
{
Console.WriteLine($"An integer is required: [{strradius}] is not an integer");
}
else if (intradius < 1)
{
Console.WriteLine($"The entered number [{intradius}] is out of range, it must be one or greater");
}
else
{
break; //breaking out of the while loop, the input is good
}
}
return intradius;
}
在 C# 代码中使用 goto 语句是非常不受欢迎的,因为它会使代码难以阅读、调试和维护(有关详细信息,请阅读 this)。可以使用循环、if/then 语句或方法调用来代替 goto 语句。此外,应谨慎使用 try \ catch
块,以捕获您无法处理的异常。
在您的情况下,我们可以使用 while
循环继续循环直到输入有效数字,然后我们可以使用 int.TryParse
method 尝试解析字符串并获取整数结果。此方法 return 是一个 Boolean
表示成功,并采用 out
参数,该参数将设置为整数结果。
我对你的方法的建议是让它接受一个字符串,该字符串将用作用户的提示(要求他们输入一个数字),return 他们输入的整数结果.
例如:
private static int GetIntFromUser(string prompt, int minValue = int.MinValue,
int maxValue = int.MaxValue)
{
int result;
string errorMsg = $"ERROR: Input must be a valid number from {minValue} to {maxValue}";
while(true)
{
Console.Write(prompt);
string input = Console.ReadLine();
if (!int.TryParse(input, out result) || result < minValue || result > maxValue)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(errorMsg);
Console.ResetColor();
}
else
{
break;
}
}
return result;
}
实际上,我们现在可以调用此方法从用户那里获取号码,我们将知道它们是有效的,而无需进行任何额外的验证:
private static void Main()
{
// Age must be between 5 and 100
var age = GetIntFromUser("Please enter your age: ", 5, 100);
// Weight must be a positive number (minimum of zero)
var weight = GetIntFromUser("Please enter your weight: ", 0);
// No restrictions on favorite number
var favNum = GetIntFromUser("Enter your favorite whole number: ");
// This is a similar method I wrote to pause the program with a prompt
GetKeyFromUser("\nDone! Press any key to exit...");
}
输出
对于可恢复验证,使用条件 code/checks 而不是依赖异常。从性能角度来看,异常是昂贵的,主要是因为它们会生成调用堆栈。
相反,看看这样的东西:
private static int GetInput(string v)
{
Console.Write(v);
string strradius = Console.ReadLine();
if (string.IsNullOrEmpty(strradius)
{
Console.WriteLine("You must enter a value.");
return 0;
}
int intradius;
bool result = int.TryParse(strradius, out intradius);
if (!result)
Console.WriteLine("You must enter a valid number.");
else if (intradius < 1)
Console.WriteLine("Your number is out of range");
Console.WriteLine("Okay");
return intradius;
}
就个人而言,我喜欢包装业务逻辑结果:
// Result container.
private class RadiusValidationResults
{
public bool IsSuccessful {get; private set;}
public int Radius {get; private set;}
public string FailureReason {get; private set;}
private RadiusValidationResults()
{ }
public static RadiusValidationResults Success(int result)
{
return new RadiusValidationResults { IsSuccessful = true, Radius = result };
}
public static RadiusValidationResults Failure(string failureReason)
{
return new RadiusValidationResults { FailureReason = failureReason };
}
}
// Validation business logic.
private static RadiusValidationResult ValidateRadius(string input)
{
if (string.IsNullOrEmpty(input)
return RadiusValidationResult.Failure("You must enter a value.");
int radius;
if (!int.TryParse(strradius, out radius))
return RadiusValidationResult.Failure("You must enter a valid number.");
else if (intradius < 1)
return RadiusValidationResult.Failure("Your number is out of range");
return RadiusValidationResult.Success(radius);
}
那么你与控制台交互的调用方法:
private static int GetInput()
{
try
{
var result = ValidateRadius(Console.ReadLine());
if(!result.IsSuccessful)
Console.WriteLine(result.FailureReason);
else
Console.WriteLine("Okay");
return result.Radius;
catch // Here you can handle specific exception types, or bubble to a higher level. Log exception details and either terminate or present users with a generic "Whoops" and let them retry the operation.
{
Console.WriteLine("An unexpected error occurred.")
}
}
这意味着您的业务逻辑(验证)可以在不硬依赖数据源或输出的情况下进行单元测试。 (控制台)代码应该简洁易懂。可以将 Try/Catch 添加到 GetInput 以处理异常情况。通常让异常冒泡到足够高的级别来处理它们。