在多个点抛出异常(重构)
Throwing exceptions at multiple points (refactoring)
我正在编写一个接受用户输入的函数,运行是我们数据库中的一个过程,并比较这些值。在此过程中,我需要检查我们是否收到了正确的输入 然后 查询是否返回了可接受的值。
private void DoTheThing(int? userInput1, int? userInput2, int valuePassedIn)
{
if (userInput1 == null || userInput2 == null)
{
Exception ex = new Exception();
ex.Data.Add("Message", "You screwed up.");
throw ex;
}
var queryResult = 0; //execute a query using the non-null inputs
if (queryResult == null) //or otherwise doesn't return an acceptable value
{
Exception ex = new Exception();
ex.Data.Add("Message", "some other thing happened");
throw ex;
}
else
{
//We're good, so do the thing
}
}
关于此的快速说明:我知道 argument against exceptions as flow control 并且我最好在我走到这一步之前检查用户的输入。我不会详细介绍所有细节,但是 请接受我在用这种方式编写函数时有点卡住了。
话虽如此,这是我的问题:
鉴于这里的 2 个异常之间的唯一区别是消息和它们被抛出的时间,我如何清理这段代码以使其既 DRY 又避免运行在确定 存在问题后宁可不必要的代码?
我考虑过使用 goto
并将错误代码放在那里,但这实际上只会解决问题。如果我将异常代码移到底部并检查 message
变量(或类似的东西),那么我只是 运行ning 代码,不需要 运行第一名。
您最好创建 BadInputException class 和 NullQueryResultException class。它们做两件不同的事情,抛出一个特定的异常比抛出一个通用的 Exception(...) 更好。事实上,我认为 FXCop 或 Visual Studio 的代码分析会给你一个关于抛出一般异常的警告。
实际上并没有那么多新代码要写。
public class BadInputException : Exception
{
public BadInputException()
{
this.Data.Add("Message", "You screwed up.")
}
}
然后代替这个:
Exception ex = new Exception();
ex.Data.Add("Message", "You screwed up.");
throw ex;
这样做:
throw new BadInputException();
编辑:将 "You screwed up" 消息从消息 属性 移至数据集合以匹配 OP 的要求。
我建议不要抛出Exception
(这意味着出了点问题,没有评论可用),但是ArgumentNullException
和InvalidOperationException
类。另一个修改是避免 arrow-head
反模式:
private void DoTheThing(int? userInput1, int? userInput2, int valuePassedIn)
{
// What actually went wrong? An argument "userInput1" is null
if (null == userInput1)
throw new ArgumentNullException("userInput1");
else if (null == userInput2)
throw new ArgumentNullException("userInput2"); // ...or userInput2 is null
var queryResult = executeSomeQuery(userInput1, userInput2, valuePassedIn);
// What went wrong? We don't expect that null can be returned;
// so the operation "executeSomeQuery" failed:
// we've provided validated (not null) values and got unexpected return.
// Let it have been known.
if (null == queryResult)
throw new InvalidOperationException(
String.Format("Query ({0}, {1}, {2}) returned null when bla-bla-bla expected",
userInput1, userInput2, valuePassedIn));
// We're good, so do the thing
// Note that's there's no "arrow-head antipattern":
// we're not within any "if" or "else" scope
}
编辑:由于每个 *Exception
都是从 Exception
继承的,所以您可以将一些信息放入 Data
:
Exception ex = new ArgumentNullException("userInput1");
ex.Data.Add("Some key", "Some value");
throw ex;
但通常 Message
是解释发生了什么的更好的地方。
我会创建一个方法:
private void CheckResult(bool cond, string msg, string info) {
if (!cond)
return;
Exception ex = new Exception();
ex.Data.Add(msg, info);
throw ex;
}
并致电
CheckResult(userInput1 == null || userInput2 == null, "Message", "You screwed up.");
和
CheckResult(queryResult == null, "Message", "You screwed up.");
我觉得这个问题Refactoring Guard Clauses对你有帮助。
Replace Nested Conditional with Guard Clauses.
中有关于此的内容
希望有用。
我正在编写一个接受用户输入的函数,运行是我们数据库中的一个过程,并比较这些值。在此过程中,我需要检查我们是否收到了正确的输入 然后 查询是否返回了可接受的值。
private void DoTheThing(int? userInput1, int? userInput2, int valuePassedIn)
{
if (userInput1 == null || userInput2 == null)
{
Exception ex = new Exception();
ex.Data.Add("Message", "You screwed up.");
throw ex;
}
var queryResult = 0; //execute a query using the non-null inputs
if (queryResult == null) //or otherwise doesn't return an acceptable value
{
Exception ex = new Exception();
ex.Data.Add("Message", "some other thing happened");
throw ex;
}
else
{
//We're good, so do the thing
}
}
关于此的快速说明:我知道 argument against exceptions as flow control 并且我最好在我走到这一步之前检查用户的输入。我不会详细介绍所有细节,但是 请接受我在用这种方式编写函数时有点卡住了。
话虽如此,这是我的问题:
鉴于这里的 2 个异常之间的唯一区别是消息和它们被抛出的时间,我如何清理这段代码以使其既 DRY 又避免运行在确定 存在问题后宁可不必要的代码?
我考虑过使用 goto
并将错误代码放在那里,但这实际上只会解决问题。如果我将异常代码移到底部并检查 message
变量(或类似的东西),那么我只是 运行ning 代码,不需要 运行第一名。
您最好创建 BadInputException class 和 NullQueryResultException class。它们做两件不同的事情,抛出一个特定的异常比抛出一个通用的 Exception(...) 更好。事实上,我认为 FXCop 或 Visual Studio 的代码分析会给你一个关于抛出一般异常的警告。
实际上并没有那么多新代码要写。
public class BadInputException : Exception
{
public BadInputException()
{
this.Data.Add("Message", "You screwed up.")
}
}
然后代替这个:
Exception ex = new Exception();
ex.Data.Add("Message", "You screwed up.");
throw ex;
这样做:
throw new BadInputException();
编辑:将 "You screwed up" 消息从消息 属性 移至数据集合以匹配 OP 的要求。
我建议不要抛出Exception
(这意味着出了点问题,没有评论可用),但是ArgumentNullException
和InvalidOperationException
类。另一个修改是避免 arrow-head
反模式:
private void DoTheThing(int? userInput1, int? userInput2, int valuePassedIn)
{
// What actually went wrong? An argument "userInput1" is null
if (null == userInput1)
throw new ArgumentNullException("userInput1");
else if (null == userInput2)
throw new ArgumentNullException("userInput2"); // ...or userInput2 is null
var queryResult = executeSomeQuery(userInput1, userInput2, valuePassedIn);
// What went wrong? We don't expect that null can be returned;
// so the operation "executeSomeQuery" failed:
// we've provided validated (not null) values and got unexpected return.
// Let it have been known.
if (null == queryResult)
throw new InvalidOperationException(
String.Format("Query ({0}, {1}, {2}) returned null when bla-bla-bla expected",
userInput1, userInput2, valuePassedIn));
// We're good, so do the thing
// Note that's there's no "arrow-head antipattern":
// we're not within any "if" or "else" scope
}
编辑:由于每个 *Exception
都是从 Exception
继承的,所以您可以将一些信息放入 Data
:
Exception ex = new ArgumentNullException("userInput1");
ex.Data.Add("Some key", "Some value");
throw ex;
但通常 Message
是解释发生了什么的更好的地方。
我会创建一个方法:
private void CheckResult(bool cond, string msg, string info) {
if (!cond)
return;
Exception ex = new Exception();
ex.Data.Add(msg, info);
throw ex;
}
并致电
CheckResult(userInput1 == null || userInput2 == null, "Message", "You screwed up.");
和
CheckResult(queryResult == null, "Message", "You screwed up.");
我觉得这个问题Refactoring Guard Clauses对你有帮助。
Replace Nested Conditional with Guard Clauses.
中有关于此的内容希望有用。