我目前正在构建一个 API,该 API 执行某个操作,该操作应指示该操作是否成功。如果操作失败,我还需要知道为什么操作会失败。我应该如何实现这个?我应该使用 void 返回类型并在发生错误时抛出异常,还是应该返回一个布尔值以及某种消息来指示它失败的原因?
6 回答
在我看来,无效和异常应该没问题。
如果失败,异常消息应该足以告诉您发生了什么。
如果成功了,那就继续你的快乐之路吧。
如果一个方法通常会被预期特定失败场景并可以处理它的代码直接调用,那么该方法应该通过除抛出异常之外的其他方式区分成功或预期失败场景。请注意,调用者不期望的失败场景在任何情况下都应该引发异常。如果某些调用代码不会期待特定的失败场景,但其他代码会,那么拥有两个版本的方法可能会有所帮助——其中一个在问题场景中引发异常,另一个返回某种其他类型的错误指示。
Microsoft 库中使用的一种常见模式是有一对方法,使用命名模式DoSomething
和TryDoSomething
. 如果方法失败,该DoSomething
方法将抛出异常。它应该提供给调用者的任何数据都将在返回值中指示。第二种方法会返回一个bool
值,表示操作是否成功;调用者的任何数据都将通过out
参数指示。这种方法在微软开始使用它时可能是合理的,但它与此后添加的语言功能交互不佳。除其他事项外:
仅将类型参数用于函数返回值的接口
IEnumerable<T>
允许相对于该类型是协变的(允许期望例如 anIEnumerable<Animal>
接受 an 的代码IEnumerable<Cat>
)。然而,协方差不适用于out
参数。如果模式是 egT TryGetSomething(ref bool Success)
而不是bool TryGetSomething(ref T Result)
,则协方差将可用于“try”方法。VB.NET 和 C# 都允许声明和赋值变量的语句根据赋值表达式推断类型。尽管这适用于返回值提供数据的方法,但它不适用于通过
out
参数向调用者提供数据的方法。
除了这些限制之外,Microsoft 模式对于您的方案可能不太理想,因为它不包含无法指示它为什么这样做的函数的机制。此外,虽然 Microsoft 建议不要使用一种方法可以作为其中之一DoSomething
并TryDoSomething
基于传入的参数,但对两者使用一种方法有一个主要优势:如果一个操作需要多个可能成功或失败的子操作,并且如果失败任何一块都意味着整个操作的失败,如果调用 from ,可能希望内部方法中的失败抛出异常DoSomething
,但如果调用 from 则返回错误代码TryDoSomething
。使用内部方法的单独“抛出”或“不抛出”变体需要复制调用它们的代码 - 恶心。
因此,我建议您可能想要定义一个INotifyOfFailure
接口并让您的方法采用可选INotifyOfFailure
参数(默认为 null)。如果发生故障且INotifyOfFailure
参数不为空,则调用其上的方法以通知其故障;它可以在内部存储有关故障的信息;函数返回后,可以询问它传入的对象是否操作成功。
确定这种情况发生的频率,如果答案经常发生,请考虑使用字符串通过引用参数来获取消息和具有布尔返回值的方法。
或者,只需有一个字符串返回值,如果成功则返回 string.Empty,并检查它的长度以了解它是成功还是失败。
第三种选择是像另一个回答者所说的那样返回一个枚举,这将表明它的原因,但不能保存来自另一个异常等的任何信息。
如果很少发生,则抛出异常。
bool
带一条消息可能是最好的。最终,异常是昂贵的——无论是你的 API 处理还是客户端处理——所以最好完全避免它们,并使用前面提到的bool
和string
.
如果发生异常(即未预料到的事情),则应抛出异常。
另一种选择是使用 anenum
这样您就可以返回不同的结果。就像是
public enum Results
{
Error,
Warning,
Minor,
}
然后他们可以使用它
var result = YourApiCall();
if (result == Result.Error)
// Do something
您可以创建一个派生自 的类EventArgs
,并添加一个Boolean
属性来检查错误,然后如果 true 有一个String
属性,他们可以从中获取错误消息。