我想从方法中返回异常,如下所示:
Exception f()
{
try
{
// Do something.
return null;
}
catch(Exception e)
{
return e;
}
}
我知道这被认为是不好的做法,但有没有任何技术原因可以避免这种情况?
编辑:这与Is it bad practice to return Exceptions from your methods不同。我问的是技术原因,而不是最佳做法。
答案 0 :(得分:1)
没有“技术”原因可以避免这种情况。也就是说,.Net框架将很乐意支持这种类型的安排。
另请注意,抛出异常会导致性能下降,因此请避免依赖于为正常控制流抛出异常。它们只应在出现真正出错的情况下使用。
在不抛出异常的情况下实例化异常也很好:
Exception f()
{
return new Exception("message");
}
答案 1 :(得分:0)
以下是您可能想要重新考虑该设计的一些原因。
首先,new ArgumentOutOfRangeException(...);
不会产生您的异常堆栈跟踪,直到您throw
为止。这可能会混淆堆栈中的调试工作。堆栈跟踪实际上就在撒谎。
其次,Exception比某种状态枚举更重要,以指示发生了什么类型的错误。很难看出你从响应对象中获得类型异常的好处是什么。它可能要求呼叫者随后执行一堆if (response is FileNotFoundException) ....
并决定他们想要为每个人做什么。
这意味着您无法区分给定函数中的预期异常和意外异常。采取一个很好理解的唯一约束违规。在您调用的某个地方或您调用的内容中,DBMS会拒绝插入语句,因为另一个记录具有此值。根据您的使用情况,这可能是一个异常情况,表明某些地方出现了非常错误(例如,损坏的数据文件),或者结果本身(例如,预订系统在向您显示之间为其他人分配了座位)它的可用性和你接受)。在第一种情况下,您希望调用者将其冒泡到应用程序中的某个点,该点知道该怎么做。在第二种情况下,您可以放心地将其返回给调用者以开展业务。
第三,这违反了最不惊讶的原则。设计上的例外情况默认为纾困。除非我明确地捕获异常,否则它将从我的方法中解脱出来,除了我的except和finally块中的东西,在任何使用块的末尾很好地调用dispose并在我的方法调用者中重复。它永远不会做的是执行可能依赖于该方法调用结果的我的代码。
默认情况下,此处的设计会被忽略。除非我明确检查结果并选择抛出,否则它将继续执行我的代码。考虑这个人为的例子:
Exception error;
error = GetData(out data);
if (error is DbException) return error;
if (error is ArgumentOutOfRangeException) return error;
// if (error is ....... etc
error = ApplyChangesToData(data);
// if (error is ....... etc
error = SaveData(data);
// if (error is ....... etc
如果我错过了什么,现在赌注很高,因为我的部分构造的数据对象可能会通过ApplyChangesToData方法,然后以损坏的形式保存回数据库。这可能很简单,因为您没有预料到内部GetData会发生SocketException或OutOfMemoryException。它不仅仅是让您的抽象有点漏洞,这意味着除非您确实有漏洞抽象,否则您的代码无法安全编写。我的代码必须知道你是在与SqlServer,Oracle还是Mongo或者其他什么对话,所以我可以预见到你可以重新编写的所有可能的异常并将它们包含在我的if行中。当然我可以把别的东西扔进去扔任何异常对象,但可能你不想这样做,或者你不会在第一时间抓住它。
另一方面,如果你允许异常冒泡,我对这种异常在我的上下文中意味着什么并且仍然有意义地继续存在有信心,我仍然可以选择捕获该异常并做出反应或抑制它。
我能想到的唯一例外(双关语)是在API服务层的边缘,你可能想要记录异常,然后返回一个通用的"服务器上出现问题"而不是诽谤你的脏衣服,并在面向公众的终端上暴露安全敏感信息。
我希望我已经说服了你(或者,如果没有,至少有人在将来阅读这个建议),不要说服那条充满龙的道路。