如何指示方法不成功
我有几种类似的方法,例如。 CalculatePoint(...)和CalculateListOfPoints(...)。有时,它们可能不会成功,并且需要向呼叫者表明这一点。对于返回通用列表的CalculateListOfPoints,我可以返回一个空列表,并要求调用者进行检查。但是Point是一个值类型,所以我不能在那里返回null。
理想情况下,我希望这些方法"看起来"相似。一种解决方案是将它们定义为
public Point CalculatePoint(... out Boolean boSuccess); public List<Point> CalculateListOfPoints(... out Boolean boSuccess);
或者返回一个点?为CalculatePoint,并返回null表示失败。但这意味着必须强制转换为不可为空的类型,这似乎过多。
另一种方法是返回布尔值boSuccess,将结果(点或者列表)作为"输出"参数,然后将它们命名为TryToCalculatePoint或者其他东西。
什么是最佳做法?
编辑:我不想使用异常进行流控制!有时可能会失败。
解决方案
我会说最佳实践是返回值表示成功,而异常则表示失败。
在我们提供的示例中,我看不出有任何理由在发生故障时不应使用异常。
就个人而言,我想我会使用与TryParse()相同的想法:使用out参数输出实际值,并返回一个布尔值,指示调用是否成功
public bool CalculatePoint(... out Point result);
我不喜欢将异常用于"正常"行为(如果我们希望该功能不适用于某些条目)。
另一种选择是引发异常。但是,我们通常只想在"例外情况"中引发例外。
如果失败的情况很普遍(而不是例外),那么我们已经列出了两个选择。编辑:项目中可能有一个约定,即如何处理此类非异常情况(无论是应返回成功还是返回对象)。如果没有现有约定,那么我同意lucasbfr并建议我们返回成功(这与TryParse(...)的设计方式相同)。
在某些情况下(尤其是在编写服务器时),使用异常是个坏主意。我们将需要两种方法。还要查看字典类,以了解我们应该做什么。
// 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; }
两全其美!
布尔TrySomething()至少是一种实践,对于.net的解析方法来说还可以,但是我认为我总体上不喜欢它。
抛出异常通常是一件好事,尽管不应将其用于在许多正常情况下可能会发生的情况,并且它会带来相关的性能成本。
在大多数情况下,当我们不希望发生异常时,可以返回null。
但是,方法有点程序性,如何创建类似PointCalculator类的东西,并在构造函数中将所需数据作为参数?然后,在其上调用CalculatePoint,并通过属性(Point和Success的单独属性)访问结果。
我使用的模型与MS在各种类的TryParse方法中使用的模型相同。
原始代码:
公共点CalculatePoint(... out布尔boSuccess);
公共列表CalculateListOfPoints(... out布尔boSuccess);
会变成
公共布尔CalculatePoint(... out(或者ref)Point CalculatedValue);
公共布尔CalculateListOfPoints(... out(或者ref)列表CalculatedValues);
基本上,我们将成功/失败作为返回值。
我们不希望在发生某些事情时抛出异常,因为@Kevin声明的异常是针对特殊情况的。
我们应该返回"失败"所预期的内容,通常我选择的不良回报为null。
方法的文档应告知用户在不计算数据时会发生什么。
总结一下,我们可以采取几种方法:
- 当返回类型是值类型(例如Point)时,请使用C#的Nullable功能并返回Point? (aka Nullable),这样我们仍然可以在失败时返回null
- 发生故障时引发异常。关于什么是"异常"和什么不是"例外"的整个争论/讨论是有争议的,这是API,我们可以决定什么是特殊的行为。
- 采用与Microsoft在Int32这样的基本类型中实现的模型相似的模型,提供CalculatePoint和TryCalculatePoint(int32.Parse和int32.TryParse),并抛出一个错误并返回一个布尔值。
- 从方法中返回具有两个属性bool Success和GenericType Value的通用结构。
根据情况,我倾向于结合使用返回null或者引发异常的组合,因为它们对我来说似乎"最干净",并且最适合与我工作的公司的现有代码库。因此,我个人的最佳实践是方法1和2.
它主要取决于方法的行为及其用法。
如果失败是常见且不严重的,则让方法返回一个指示成功的布尔值,并使用out参数来传达结果。在哈希中查找键,在没有可用数据时尝试在非阻塞套接字上读取数据,所有这些示例都属于该类别。
如果意外失败,请直接返回结果并传达异常情况下的错误。最好以只读方式打开文件,然后连接到TCP服务器。
有时两种方式都有意义...
他们为什么会失败?如果是由于调用者已完成的操作(即提供的参数),则抛出ArgumentException是完全合适的。避免异常的Try [...]方法很好。
我认为提供一个引发异常的版本是一个好主意,这样,如果希望他们总是提供良好的数据的调用者犯错了,则会收到适当的强烈信息(即异常)。
如果失败是由于特定原因,那么我认为可以返回null或者bool并具有out参数。但是,如果无论失败如何都返回null,那么我不建议我们这样做。异常提供了丰富的信息,包括为什么某些原因失败的原因,如果返回的所有结果都是null,那么如何知道是否由于数据错误,内存不足或者其他怪异行为。
即使在.net中,TryParse都有一个Parse兄弟,因此我们可以根据需要获取异常。
如果我提供了TrySomething方法,那么我还将提供Something方法,该方法在发生故障时会引发异常。然后由调用者决定。
我们曾经编写了一个完整的Framework,其中所有公共方法或者返回true(成功执行),或者返回false(发生错误)。如果需要返回值,则使用输出参数。与普遍的看法相反,这种编程方式实际上简化了许多代码。
使用Point,可以在失败的情况下将Point.Empty作为返回值发送回去。现在,所有这些真正要做的就是为X和Y值返回一个带有0的点,因此,如果这可以是一个有效的返回值,我将尽量避免这样做,但是如果方法永远不会返回(0,0) ,那么我们可以使用它。
返回Point.Empty。当我们要检查结构创建是否成功时,这是一个.NET设计模式,可以返回一个特殊字段。尽可能避免使用掉参数。
public static readonly Point Empty
我正在尝试的一种模式正在返回Maybe。它具有TryParse模式的语义,但与错误时返回null模式相似。
我尚未说服其中一种方法,但我将其提供给我们集体考虑。这样做的好处是,不需要在方法调用之前定义变量即可将out参数保存在方法的调用位置。它也可以通过Errors或者Messages集合进行扩展,以指示失败的原因。
Maybe类看起来像这样:
/// <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; } } }
抱歉,我只记得Nullable类型,应该看看。我不太确定那是什么开销。