How to indicate that a method was unsuccessful
我有几个类似的方法,例如calculatePoint(…)和calculateListofPoints(…)。有时,他们可能不会成功,需要向调用者表明这一点。对于CalculateListOfPoints,它返回一个通用列表,我可以返回一个空列表,并要求调用者检查它;但是,点是一个值类型,因此我不能在那里返回空值。
理想情况下,我希望方法"看起来"相似;一种解决方案是将它们定义为
1 2 | public Point CalculatePoint(... out Boolean boSuccess); public List<Point> CalculateListOfPoints(... out Boolean boSuccess); |
或者返回一个点?对于CalculatePoint,返回空以指示失败。这意味着必须将类型转换回不可为空的类型,但这似乎太过分了。
另一种方法是返回Boolean Bosuccess,将结果(点或列表)作为"out"参数,并调用它们TryToCalculatePoint或其他…
什么是最佳实践?
编辑:我不想使用例外流控制!失败有时是意料之中的。
就我个人而言,我想我会使用与typarse()相同的想法:使用out参数输出实际值,并返回一个布尔值,指示调用是否成功。
我不喜欢将异常用于"正常"行为(如果您希望函数对某些条目不起作用)。
他们为什么会失败?如果是因为调用者做了什么(即提供的参数),那么抛出ArgumentException是完全合适的。可以使用try[…]方法避免异常。
我认为提供引发异常的版本是一个好主意,这样,期望始终提供良好数据的呼叫者在出错时会收到适当的强消息(即异常)。
另一种选择是抛出异常。但是,您通常只希望在"异常情况"中抛出异常。
如果失败案例很常见(并且不是例外),那么您已经列出了两个选项。编辑:在您的项目中可能有一个惯例,即如何处理这些非异常情况(一个人应该返回成功还是对象)。如果没有现有的约定,那么我同意Lucasbfr并建议您返回成功(这与Terparse(…)的设计方式一致)。
如果失败是由于特定的原因,那么我认为返回空值或bool并有一个out参数是可以的。但是,如果不管失败如何都返回空值,那么我不建议这样做。异常提供了一组丰富的信息,包括一些失败的原因,如果您得到的全部是一个空值,那么您如何知道它是否是因为数据错误、内存不足或其他一些奇怪的行为。
即使在.NET中,台盼也有一个解析兄弟,这样您可以在需要时获取异常。
如果我提供一个Trysometing方法,我还将提供一个在失败时引发异常的方法。然后由来电者决定。
它主要取决于方法的行为和它们的用法。
如果失败是常见的和非关键的,那么让您的方法返回一个指示其成功的布尔值,并使用out参数来传递结果。在散列中查找键,在没有可用数据的情况下尝试读取非阻塞套接字上的数据,所有这些示例都属于该类别。
如果失败是意外的,则直接返回结果并传递错误和异常。以只读方式打开文件(连接到TCP服务器)是很好的选择。
有时这两种方法都有意义…
我使用的模型与MS使用的不同类的台盼色方法相同。
您的原始代码:公共点计算点(…输出布尔值bosuccess);公共列表CalculateListofPoints(…输出布尔值bosuccess);
会变成公共布尔计算点(…输出(或参考)点计算值;公共bool calculateListofPoints(…列出(或参考)计算值;
基本上,您将成功/失败作为返回值。
总结一下,您可以采取以下几种方法:
根据场景的不同,我倾向于使用返回空值或抛出异常的组合,因为它们对我来说是"最干净的",并且最适合我工作的公司中现有的代码库。所以我个人的最佳实践是方法1和2。
返回点。空。当您想检查结构创建是否成功时,返回一个特殊字段是.NET设计模式。尽可能避免输出参数。
1 | public static readonly Point Empty |
我正在试验的一个模式是返回一个可能。它具有台盼模式的语义,但与错误模式的空返回类似。
我还没有以这种或那种方式说服你,但我提出这个建议是为了你的集体考虑。它的好处是不需要在方法调用之前定义一个变量,以便在方法的调用站点保存out参数。它还可以通过错误或消息集合进行扩展,以指示失败的原因。
可能的类如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /// <summary> /// Represents the return value from an operation that might fail /// </summary> /// <typeparam name="T"></typeparam> public struct Maybe<T> { T _value; bool _hasValue; public Maybe(T value) { _value = value; _hasValue = true; } public Maybe() { _hasValue = false; _value = default(T); } public bool Success { get { return _hasValue; } } public T Value { get { // could throw an exception if _hasValue is false return _value; } } } |
对不起,我刚刚记起了可以为空的类型,你应该看看。不过,我不太确定开销是多少。
我们曾经编写了一个完整的框架,其中所有公共方法都返回了true(成功执行)或false(发生错误)。如果需要返回值,则使用输出参数。与流行的观点相反,这种编程方式实际上简化了我们的许多代码。
我会说,最佳实践是回报值意味着成功,而例外意味着失败。
在您提供的示例中,我看不到在发生故障时不应使用异常的原因。
使用point,你可以返回point.empty作为失败时的返回值。现在,所有这些实际上都是返回一个带有0的点作为x和y值,所以如果这是一个有效的返回值,我会远离它,但是如果你的方法永远不会返回一个(0,0)点,那么你可以使用它。
bool trysomething()至少是一种实践,对于.NET的解析方法来说是可行的,但我不认为我一般喜欢它。
抛出一个异常通常是一件好事,尽管它不应该用于您期望在许多正常情况下发生的情况,并且它有一个相关的性能成本。
在大多数情况下,当不需要异常时,尽可能返回空值是可以的。
但是,您的方法有点程序化——创建点计算器类之类的东西怎么样——将所需数据作为构造函数中的参数?然后对其调用calculatePoint,并通过属性访问结果(分别为point和success属性)。
在某些情况下(尤其是在编写服务器时),使用异常是一个坏主意。您需要两种风格的方法。还要查看Dictionary类以了解应该做什么。
1 2 3 4 5 6 7 8 9 10 11 | // NB: A bool is the return value. // This makes it possible to put this beast in if statements. public bool TryCalculatePoint(... out Point result) { } public Point CalculatePoint(...) { Point result; if(!TryCalculatePoint(... out result)) throw new BogusPointException(); return result; } |
两全其美!
您不希望在发生预期情况时抛出异常,正如@kevin所说的异常是针对异常情况的。
你应该返回一些"失败"所期望的东西,通常空值是我选择的错误返回。
您方法的文档应该告诉用户当数据不计算时会发生什么。